fix: merge yudao/dev 解决冲突

This commit is contained in:
jason
2025-06-09 00:04:45 +08:00
316 changed files with 19685 additions and 4419 deletions

View File

@@ -74,6 +74,16 @@ setupVbenVxeTable({
}, },
}); });
vxeUI.renderer.add('CellImages', {
renderTableDefault(_renderOpts, params) {
const { column, row } = params;
if (column && column.field && row[column.field]) {
return row[column.field].map((item: any) => h(Image, { src: item }));
}
return '';
},
});
// 表格配置项可以用 cellRender: { name: 'CellLink' }, // 表格配置项可以用 cellRender: { name: 'CellLink' },
vxeUI.renderer.add('CellLink', { vxeUI.renderer.add('CellLink', {
renderTableDefault(renderOpts) { renderTableDefault(renderOpts) {
@@ -267,6 +277,38 @@ setupVbenVxeTable({
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
// vxeUI.formats.add // vxeUI.formats.add
vxeUI.formats.add('formatPast2', {
tableCellFormatMethod({ cellValue }) {
if (cellValue === null || cellValue === undefined) {
return '';
}
// 定义时间单位常量,便于维护
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
// 计算各时间单位
const day = Math.floor(cellValue / DAY);
const hour = Math.floor((cellValue % DAY) / HOUR);
const minute = Math.floor((cellValue % HOUR) / MINUTE);
const second = Math.floor((cellValue % MINUTE) / SECOND);
// 根据时间长短返回不同格式
if (day > 0) {
return `${day}${hour} 小时 ${minute} 分钟`;
}
if (hour > 0) {
return `${hour} 小时 ${minute} 分钟`;
}
if (minute > 0) {
return `${minute} 分钟`;
}
return second > 0 ? `${second}` : `${0}`;
},
});
// add by 星语:数量格式化,例如说:金额 // add by 星语:数量格式化,例如说:金额
vxeUI.formats.add('formatNumber', { vxeUI.formats.add('formatNumber', {
tableCellFormatMethod({ cellValue }, digits = 2) { tableCellFormatMethod({ cellValue }, digits = 2) {
@@ -284,7 +326,7 @@ setupVbenVxeTable({
}, },
}); });
vxeUI.formats.add('formatFraction', { vxeUI.formats.add('formatAmount2', {
tableCellFormatMethod({ cellValue }) { tableCellFormatMethod({ cellValue }) {
if (cellValue === null || cellValue === undefined) { if (cellValue === null || cellValue === undefined) {
return '0.00'; return '0.00';

View File

@@ -3,7 +3,8 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmCategoryApi { export namespace BpmCategoryApi {
/** BPM 流程分类 VO */ /** 流程分类 VO */
// TODO @jason不用 VO 后缀哈
export interface CategoryVO { export interface CategoryVO {
id: number; id: number;
name: string; name: string;

View File

@@ -2,9 +2,9 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
/** 流程定义 */
export namespace BpmProcessDefinitionApi { export namespace BpmProcessDefinitionApi {
// 流程定义 /** 流程定义 */
// TODO @ziye不用 VO 后缀哈
export interface ProcessDefinitionVO { export interface ProcessDefinitionVO {
id: string; id: string;
version: number; version: number;

View File

@@ -3,7 +3,8 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmFormApi { export namespace BpmFormApi {
// 流程表单 /** 流程表单 */
// TODO @jason不用 VO 后缀哈
export interface FormVO { export interface FormVO {
id?: number | undefined; id?: number | undefined;
name: string; name: string;
@@ -23,6 +24,7 @@ export async function getFormPage(params: PageParam) {
} }
/** 获取表单详情 */ /** 获取表单详情 */
// TODO @ziye应该不会 string 的情况呢。
export async function getFormDetail(id: number | string) { export async function getFormDetail(id: number | string) {
return requestClient.get<BpmFormApi.FormVO>(`/bpm/form/get?id=${id}`); return requestClient.get<BpmFormApi.FormVO>(`/bpm/form/get?id=${id}`);
} }

View File

@@ -12,6 +12,7 @@ export namespace BpmModelApi {
} }
/** 流程定义 VO */ /** 流程定义 VO */
// TODO @jason不用 VO 后缀哈
export interface ProcessDefinitionVO { export interface ProcessDefinitionVO {
id: string; id: string;
key?: string; key?: string;

View File

@@ -3,6 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmOALeaveApi { export namespace BpmOALeaveApi {
// TODO @ziye不用 VO 后缀
export interface LeaveVO { export interface LeaveVO {
id: number; id: number;
status: number; status: number;

View File

@@ -3,7 +3,8 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmProcessExpressionApi { export namespace BpmProcessExpressionApi {
/** BPM 流程表达式 VO */ // TODO @ziye不用 VO 后缀
/** 流程表达式 VO */
export interface ProcessExpressionVO { export interface ProcessExpressionVO {
id: number; // 编号 id: number; // 编号
name: string; // 表达式名字 name: string; // 表达式名字

View File

@@ -8,6 +8,7 @@ import type { BpmCandidateStrategyEnum, BpmNodeTypeEnum } from '#/utils';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmProcessInstanceApi { export namespace BpmProcessInstanceApi {
// TODO @芋艿:一些注释缺少或者不对;
export type Task = { export type Task = {
id: number; id: number;
name: string; name: string;
@@ -42,7 +43,7 @@ export namespace BpmProcessInstanceApi {
tasks: ApprovalTaskInfo[]; tasks: ApprovalTaskInfo[];
}; };
// 流程实例 /** 流程实例 */
export type ProcessInstanceVO = { export type ProcessInstanceVO = {
businessKey: string; businessKey: string;
category: string; category: string;

View File

@@ -3,6 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmProcessListenerApi { export namespace BpmProcessListenerApi {
// TODO @ziye不用 VO 后缀
/** BPM 流程监听器 VO */ /** BPM 流程监听器 VO */
export interface ProcessListenerVO { export interface ProcessListenerVO {
id: number; // 编号 id: number; // 编号

View File

@@ -5,6 +5,7 @@ import type { BpmProcessInstanceApi } from '../processInstance';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmTaskApi { export namespace BpmTaskApi {
// TODO @ziye不用 VO 后缀;注释使用 /** */ 风格;
/** BPM 流程监听器 VO */ /** BPM 流程监听器 VO */
export interface TaskVO { export interface TaskVO {
id: number; // 编号 id: number; // 编号

View File

@@ -3,6 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmUserGroupApi { export namespace BpmUserGroupApi {
// TODO @ziye不用 VO 后缀
/** BPM 用户组 VO */ /** BPM 用户组 VO */
export interface UserGroupVO { export interface UserGroupVO {
id: number; id: number;

View File

@@ -47,8 +47,7 @@ export function deleteDemo01Contact(id: number) {
} }
/** 批量删除示例联系人 */ /** 批量删除示例联系人 */
// TODO @puhui999ByIds这种按照约定是不带的针对 Id 的情况哈。 export function deleteDemo01ContactList(ids: number[]) {
export function deleteDemo01ContactListByIds(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo01-contact/delete-list?ids=${ids.join(',')}`, `/infra/demo01-contact/delete-list?ids=${ids.join(',')}`,
); );

View File

@@ -62,7 +62,7 @@ export function deleteDemo03Student(id: number) {
} }
/** 批量删除学生 */ /** 批量删除学生 */
export function deleteDemo03StudentListByIds(ids: number[]) { export function deleteDemo03StudentList(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`, `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`,
); );
@@ -109,7 +109,7 @@ export function deleteDemo03Course(id: number) {
} }
/** 批量删除学生课程 */ /** 批量删除学生课程 */
export function deleteDemo03CourseListByIds(ids: number[]) { export function deleteDemo03CourseList(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`, `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`,
); );
@@ -155,7 +155,7 @@ export function deleteDemo03Grade(id: number) {
} }
/** 批量删除学生班级 */ /** 批量删除学生班级 */
export function deleteDemo03GradeListByIds(ids: number[]) { export function deleteDemo03GradeList(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`, `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`,
); );

View File

@@ -64,7 +64,7 @@ export function deleteDemo03Student(id: number) {
} }
/** 批量删除学生 */ /** 批量删除学生 */
export function deleteDemo03StudentListByIds(ids: number[]) { export function deleteDemo03StudentList(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`, `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`,
); );

View File

@@ -64,7 +64,7 @@ export function deleteDemo03Student(id: number) {
} }
/** 批量删除学生 */ /** 批量删除学生 */
export function deleteDemo03StudentListByIds(ids: number[]) { export function deleteDemo03StudentList(ids: number[]) {
return requestClient.delete( return requestClient.delete(
`/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`, `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`,
); );

View File

@@ -0,0 +1,47 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBannerApi {
/** Banner 信息 */
export interface Banner {
id: number;
title: string;
picUrl: string;
status: number;
url: string;
position: number;
sort: number;
memo: string;
}
}
/** 查询Banner管理列表 */
export function getBannerPage(params: PageParam) {
return requestClient.get<PageResult<MallBannerApi.Banner>>(
'/promotion/banner/page',
{ params },
);
}
/** 查询Banner管理详情 */
export function getBanner(id: number) {
return requestClient.get<MallBannerApi.Banner>(
`/promotion/banner/get?id=${id}`,
);
}
/** 新增Banner管理 */
export function createBanner(data: MallBannerApi.Banner) {
return requestClient.post('/promotion/banner/create', data);
}
/** 修改Banner管理 */
export function updateBanner(data: MallBannerApi.Banner) {
return requestClient.put('/promotion/banner/update', data);
}
/** 删除Banner管理 */
export function deleteBanner(id: number) {
return requestClient.delete(`/promotion/banner/delete?id=${id}`);
}

View File

@@ -0,0 +1,58 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBrandApi {
/** 商品品牌 */
export interface Brand {
/** 品牌编号 */
id?: number;
/** 品牌名称 */
name: string;
/** 品牌图片 */
picUrl: string;
/** 品牌排序 */
sort?: number;
/** 品牌描述 */
description?: string;
/** 开启状态 */
status: number;
}
}
/** 创建商品品牌 */
export function createBrand(data: MallBrandApi.Brand) {
return requestClient.post('/product/brand/create', data);
}
/** 更新商品品牌 */
export function updateBrand(data: MallBrandApi.Brand) {
return requestClient.put('/product/brand/update', data);
}
/** 删除商品品牌 */
export function deleteBrand(id: number) {
return requestClient.delete(`/product/brand/delete?id=${id}`);
}
/** 获得商品品牌 */
export function getBrand(id: number) {
return requestClient.get<MallBrandApi.Brand>(`/product/brand/get?id=${id}`);
}
/** 获得商品品牌列表 */
export function getBrandPage(params: PageParam) {
return requestClient.get<PageResult<MallBrandApi.Brand>>(
'/product/brand/page',
{
params,
},
);
}
/** 获得商品品牌精简信息列表 */
export function getSimpleBrandList() {
return requestClient.get<MallBrandApi.Brand[]>(
'/product/brand/list-all-simple',
);
}

View File

@@ -0,0 +1,51 @@
import { requestClient } from '#/api/request';
export namespace MallCategoryApi {
/** 产品分类 */
export interface Category {
/** 分类编号 */
id?: number;
/** 父分类编号 */
parentId?: number;
/** 分类名称 */
name: string;
/** 移动端分类图 */
picUrl: string;
/** 分类排序 */
sort: number;
/** 开启状态 */
status: number;
}
}
/** 创建商品分类 */
export function createCategory(data: MallCategoryApi.Category) {
return requestClient.post('/product/category/create', data);
}
/** 更新商品分类 */
export function updateCategory(data: MallCategoryApi.Category) {
return requestClient.put('/product/category/update', data);
}
/** 删除商品分类 */
export function deleteCategory(id: number) {
return requestClient.delete(`/product/category/delete?id=${id}`);
}
/** 获得商品分类 */
export function getCategory(id: number) {
return requestClient.get<MallCategoryApi.Category>(
`/product/category/get?id=${id}`,
);
}
/** 获得商品分类列表 */
export function getCategoryList(params: any) {
return requestClient.get<MallCategoryApi.Category[]>(
'/product/category/list',
{
params,
},
);
}

View File

@@ -0,0 +1,81 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallCommentApi {
export interface Property {
propertyId: number;
propertyName: string;
valueId: number;
valueName: string;
}
/** 商品评论 */
export interface Comment {
id: number;
userId: number;
userNickname: string;
userAvatar: string;
anonymous: boolean;
orderId: number;
orderItemId: number;
spuId: number;
spuName: string;
skuId: number;
visible: boolean;
scores: number;
descriptionScores: number;
benefitScores: number;
content: string;
picUrls: string[];
replyStatus: boolean;
replyUserId: number;
replyContent: string;
replyTime: Date;
createTime: Date;
skuProperties: Property[];
}
/** 评论可见性更新 */
export interface CommentVisibleUpdate {
id: number;
visible: boolean;
}
/** 评论回复 */
export interface CommentReply {
id: number;
replyContent: string;
}
}
/** 查询商品评论列表 */
export function getCommentPage(params: PageParam) {
return requestClient.get<PageResult<MallCommentApi.Comment>>(
'/product/comment/page',
{ params },
);
}
/** 查询商品评论详情 */
export function getComment(id: number) {
return requestClient.get<MallCommentApi.Comment>(
`/product/comment/get?id=${id}`,
);
}
/** 添加自评 */
export function createComment(data: MallCommentApi.Comment) {
return requestClient.post('/product/comment/create', data);
}
/** 显示 / 隐藏评论 */
export function updateCommentVisible(
data: MallCommentApi.CommentVisibleUpdate,
) {
return requestClient.put('/product/comment/update-visible', data);
}
/** 商家回复 */
export function replyComment(data: MallCommentApi.CommentReply) {
return requestClient.put('/product/comment/reply', data);
}

View File

@@ -0,0 +1,23 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallFavoriteApi {
/** 商品收藏 */
export interface Favorite {
/** 收藏编号 */
id?: number;
/** 用户编号 */
userId?: string;
/** 商品 SPU 编号 */
spuId?: null | number;
}
}
/** 获得商品收藏列表 */
export function getFavoritePage(params: PageParam) {
return requestClient.get<PageResult<MallFavoriteApi.Favorite>>(
'/product/favorite/page',
{ params },
);
}

View File

@@ -0,0 +1,29 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallHistoryApi {
/** 商品浏览记录 */
export interface BrowseHistory {
/** 记录编号 */
id?: number;
/** 用户编号 */
userId?: number;
/** 商品 SPU 编号 */
spuId?: number;
/** 浏览时间 */
createTime?: Date;
}
}
/**
* 获得商品浏览记录分页
*
* @param params 请求参数
*/
export function getBrowseHistoryPage(params: PageParam) {
return requestClient.get<PageResult<MallHistoryApi.BrowseHistory>>(
'/product/browse-history/page',
{ params },
);
}

View File

@@ -0,0 +1,111 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallPropertyApi {
/** 商品属性 */
export interface Property {
/** 属性编号 */
id?: number;
/** 名称 */
name: string;
/** 备注 */
remark?: string;
}
/** 属性值 */
export interface PropertyValue {
/** 属性值编号 */
id?: number;
/** 属性项的编号 */
propertyId?: number;
/** 名称 */
name: string;
/** 备注 */
remark?: string;
}
/** 属性值查询参数 */
export interface PropertyValueQuery extends PageParam {
propertyId?: number;
}
}
/** 创建属性项 */
export function createProperty(data: MallPropertyApi.Property) {
return requestClient.post('/product/property/create', data);
}
/** 更新属性项 */
export function updateProperty(data: MallPropertyApi.Property) {
return requestClient.put('/product/property/update', data);
}
/** 删除属性项 */
export function deleteProperty(id: number) {
return requestClient.delete(`/product/property/delete?id=${id}`);
}
/** 获得属性项 */
export function getProperty(id: number) {
return requestClient.get<MallPropertyApi.Property>(
`/product/property/get?id=${id}`,
);
}
/** 获得属性项分页 */
export function getPropertyPage(params: PageParam) {
return requestClient.get<PageResult<MallPropertyApi.Property>>(
'/product/property/page',
{ params },
);
}
/** 获得属性项精简列表 */
export function getPropertySimpleList() {
return requestClient.get<MallPropertyApi.Property[]>(
'/product/property/simple-list',
);
}
/** 获得属性值分页 */
export function getPropertyValuePage(
params: MallPropertyApi.PropertyValueQuery,
) {
return requestClient.get<PageResult<MallPropertyApi.PropertyValue>>(
'/product/property/value/page',
{ params },
);
}
/** 获得属性值 */
export function getPropertyValue(id: number) {
return requestClient.get<MallPropertyApi.PropertyValue>(
`/product/property/value/get?id=${id}`,
);
}
/** 创建属性值 */
export function createPropertyValue(data: MallPropertyApi.PropertyValue) {
return requestClient.post('/product/property/value/create', data);
}
/** 更新属性值 */
export function updatePropertyValue(data: MallPropertyApi.PropertyValue) {
return requestClient.put('/product/property/value/update', data);
}
/** 删除属性值 */
export function deletePropertyValue(id: number) {
return requestClient.delete(`/product/property/value/delete?id=${id}`);
}
/** 获得属性值精简列表 */
export function getPropertyValueSimpleList(propertyId: number) {
return requestClient.get<MallPropertyApi.PropertyValue[]>(
'/product/property/value/simple-list',
{
params: { propertyId },
},
);
}

View File

@@ -0,0 +1,177 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallSpuApi {
/** 商品属性 */
export interface Property {
/** 属性编号 */
propertyId?: number;
/** 属性名称 */
propertyName?: string;
/** 属性值编号 */
valueId?: number;
/** 属性值名称 */
valueName?: string;
}
/** 商品 SKU */
export interface Sku {
/** 商品 SKU 编号 */
id?: number;
/** 商品 SKU 名称 */
name?: string;
/** SPU 编号 */
spuId?: number;
/** 属性数组 */
properties?: Property[];
/** 商品价格 */
price?: number | string;
/** 市场价 */
marketPrice?: number | string;
/** 成本价 */
costPrice?: number | string;
/** 商品条码 */
barCode?: string;
/** 图片地址 */
picUrl?: string;
/** 库存 */
stock?: number;
/** 商品重量单位kg 千克 */
weight?: number;
/** 商品体积单位m^3 平米 */
volume?: number;
/** 一级分销的佣金 */
firstBrokeragePrice?: number | string;
/** 二级分销的佣金 */
secondBrokeragePrice?: number | string;
/** 商品销量 */
salesCount?: number;
}
/** 优惠券模板 */
export interface GiveCouponTemplate {
/** 优惠券编号 */
id?: number;
/** 优惠券名称 */
name?: string;
}
/** 商品 SPU */
export interface Spu {
/** 商品编号 */
id?: number;
/** 商品名称 */
name?: string;
/** 商品分类 */
categoryId?: number;
/** 关键字 */
keyword?: string;
/** 单位 */
unit?: number | undefined;
/** 商品封面图 */
picUrl?: string;
/** 商品轮播图 */
sliderPicUrls?: string[];
/** 商品简介 */
introduction?: string;
/** 配送方式 */
deliveryTypes?: number[];
/** 运费模版 */
deliveryTemplateId?: number | undefined;
/** 商品品牌编号 */
brandId?: number;
/** 商品规格 */
specType?: boolean;
/** 分销类型 */
subCommissionType?: boolean;
/** sku数组 */
skus?: Sku[];
/** 商品详情 */
description?: string;
/** 商品排序 */
sort?: number;
/** 赠送积分 */
giveIntegral?: number;
/** 虚拟销量 */
virtualSalesCount?: number;
/** 商品价格 */
price?: number;
/** 商品拼团价格 */
combinationPrice?: number;
/** 商品秒杀价格 */
seckillPrice?: number;
/** 商品销量 */
salesCount?: number;
/** 市场价 */
marketPrice?: number;
/** 成本价 */
costPrice?: number;
/** 商品库存 */
stock?: number;
/** 商品创建时间 */
createTime?: Date;
/** 商品状态 */
status?: number;
}
/** 商品状态更新 */
export interface StatusUpdate {
/** 商品编号 */
id: number;
/** 商品状态 */
status: number;
}
}
/** 获得商品 SPU 列表 */
export function getSpuPage(params: PageParam) {
return requestClient.get<PageResult<MallSpuApi.Spu>>('/product/spu/page', {
params,
});
}
/** 获得商品 SPU 列表 tabsCount */
export function getTabsCount() {
return requestClient.get<Record<string, number>>('/product/spu/get-count');
}
/** 创建商品 SPU */
export function createSpu(data: MallSpuApi.Spu) {
return requestClient.post('/product/spu/create', data);
}
/** 更新商品 SPU */
export function updateSpu(data: MallSpuApi.Spu) {
return requestClient.put('/product/spu/update', data);
}
/** 更新商品 SPU 状态 */
export function updateStatus(data: MallSpuApi.StatusUpdate) {
return requestClient.put('/product/spu/update-status', data);
}
/** 获得商品 SPU */
export function getSpu(id: number) {
return requestClient.get<MallSpuApi.Spu>(`/product/spu/get-detail?id=${id}`);
}
/** 获得商品 SPU 详情列表 */
export function getSpuDetailList(ids: number[]) {
return requestClient.get<MallSpuApi.Spu[]>(`/product/spu/list?spuIds=${ids}`);
}
/** 删除商品 SPU */
export function deleteSpu(id: number) {
return requestClient.delete(`/product/spu/delete?id=${id}`);
}
/** 导出商品 SPU Excel */
export function exportSpu(params: PageParam) {
return requestClient.download('/product/spu/export', { params });
}
/** 获得商品 SPU 精简列表 */
export function getSpuSimpleList() {
return requestClient.get<MallSpuApi.Spu[]>('/product/spu/list-all-simple');
}

View File

@@ -0,0 +1,65 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallArticleApi {
/** 文章管理 */
export interface Article {
/** 文章编号 */
id: number;
/** 分类编号 */
categoryId: number;
/** 文章标题 */
title: string;
/** 作者 */
author: string;
/** 封面图 */
picUrl: string;
/** 文章简介 */
introduction: string;
/** 浏览数量 */
browseCount: string;
/** 排序 */
sort: number;
/** 状态 */
status: number;
/** 商品编号 */
spuId: number;
/** 是否热门 */
recommendHot: boolean;
/** 是否轮播图 */
recommendBanner: boolean;
/** 文章内容 */
content: string;
}
}
/** 查询文章管理列表 */
export function getArticlePage(params: PageParam) {
return requestClient.get<PageResult<MallArticleApi.Article>>(
'/promotion/article/page',
{ params },
);
}
/** 查询文章管理详情 */
export function getArticle(id: number) {
return requestClient.get<MallArticleApi.Article>(
`/promotion/article/get?id=${id}`,
);
}
/** 新增文章管理 */
export function createArticle(data: MallArticleApi.Article) {
return requestClient.post('/promotion/article/create', data);
}
/** 修改文章管理 */
export function updateArticle(data: MallArticleApi.Article) {
return requestClient.put('/promotion/article/update', data);
}
/** 删除文章管理 */
export function deleteArticle(id: number) {
return requestClient.delete(`/promotion/article/delete?id=${id}`);
}

View File

@@ -0,0 +1,60 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallArticleCategoryApi {
/** 文章分类 */
export interface ArticleCategory {
/** 分类编号 */
id: number;
/** 分类名称 */
name: string;
/** 分类图片 */
picUrl: string;
/** 状态 */
status: number;
/** 排序 */
sort: number;
}
}
/** 查询文章分类列表 */
export function getArticleCategoryPage(params: PageParam) {
return requestClient.get<PageResult<MallArticleCategoryApi.ArticleCategory>>(
'/promotion/article-category/page',
{ params },
);
}
/** 查询文章分类精简信息列表 */
export function getSimpleArticleCategoryList() {
return requestClient.get<MallArticleCategoryApi.ArticleCategory[]>(
'/promotion/article-category/list-all-simple',
);
}
/** 查询文章分类详情 */
export function getArticleCategory(id: number) {
return requestClient.get<MallArticleCategoryApi.ArticleCategory>(
`/promotion/article-category/get?id=${id}`,
);
}
/** 新增文章分类 */
export function createArticleCategory(
data: MallArticleCategoryApi.ArticleCategory,
) {
return requestClient.post('/promotion/article-category/create', data);
}
/** 修改文章分类 */
export function updateArticleCategory(
data: MallArticleCategoryApi.ArticleCategory,
) {
return requestClient.put('/promotion/article-category/update', data);
}
/** 删除文章分类 */
export function deleteArticleCategory(id: number) {
return requestClient.delete(`/promotion/article-category/delete?id=${id}`);
}

View File

@@ -0,0 +1,106 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { requestClient } from '#/api/request';
export namespace MallBargainActivityApi {
/** 砍价活动 */
export interface BargainActivity {
/** 活动编号 */
id?: number;
/** 活动名称 */
name?: string;
/** 开始时间 */
startTime?: Date;
/** 结束时间 */
endTime?: Date;
/** 状态 */
status?: number;
/** 达到该人数,才能砍到低价 */
helpMaxCount?: number;
/** 最大帮砍次数 */
bargainCount?: number;
/** 最大购买次数 */
totalLimitCount?: number;
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SKU 编号 */
skuId: number;
/** 砍价起始价格,单位分 */
bargainFirstPrice: number;
/** 砍价底价 */
bargainMinPrice: number;
/** 活动库存 */
stock: number;
/** 用户每次砍价的最小金额,单位:分 */
randomMinPrice?: number;
/** 用户每次砍价的最大金额,单位:分 */
randomMaxPrice?: number;
}
/** 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装 */
export interface BargainProduct {
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SKU 编号 */
skuId: number;
/** 砍价起始价格,单位分 */
bargainFirstPrice: number;
/** 砍价底价 */
bargainMinPrice: number;
/** 活动库存 */
stock: number;
}
/** 扩展 SKU 配置 */
export type SkuExtension = {
/** 砍价活动配置 */
productConfig: BargainProduct;
} & MallSpuApi.Sku;
/** 扩展 SPU 配置 */
export interface SpuExtension extends MallSpuApi.Spu {
/** SKU 列表 */
skus: SkuExtension[];
}
}
/** 查询砍价活动列表 */
export function getBargainActivityPage(params: PageParam) {
return requestClient.get<PageResult<MallBargainActivityApi.BargainActivity>>(
'/promotion/bargain-activity/page',
{ params },
);
}
/** 查询砍价活动详情 */
export function getBargainActivity(id: number) {
return requestClient.get<MallBargainActivityApi.BargainActivity>(
`/promotion/bargain-activity/get?id=${id}`,
);
}
/** 新增砍价活动 */
export function createBargainActivity(
data: MallBargainActivityApi.BargainActivity,
) {
return requestClient.post('/promotion/bargain-activity/create', data);
}
/** 修改砍价活动 */
export function updateBargainActivity(
data: MallBargainActivityApi.BargainActivity,
) {
return requestClient.put('/promotion/bargain-activity/update', data);
}
/** 关闭砍价活动 */
export function closeBargainActivity(id: number) {
return requestClient.put(`/promotion/bargain-activity/close?id=${id}`);
}
/** 删除砍价活动 */
export function deleteBargainActivity(id: number) {
return requestClient.delete(`/promotion/bargain-activity/delete?id=${id}`);
}

View File

@@ -0,0 +1,27 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBargainHelpApi {
/** 砍价记录 */
export interface BargainHelp {
/** 记录编号 */
id: number;
/** 砍价记录编号 */
record: number;
/** 用户编号 */
userId: number;
/** 砍掉金额 */
reducePrice: number;
/** 结束时间 */
endTime: Date;
}
}
/** 查询砍价记录列表 */
export function getBargainHelpPage(params: PageParam) {
return requestClient.get<PageResult<MallBargainHelpApi.BargainHelp>>(
'/promotion/bargain-help/page',
{ params },
);
}

View File

@@ -0,0 +1,37 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBargainRecordApi {
/** 砍价记录 */
export interface BargainRecord {
/** 记录编号 */
id: number;
/** 活动编号 */
activityId: number;
/** 用户编号 */
userId: number;
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SKU 编号 */
skuId: number;
/** 砍价起始价格 */
bargainFirstPrice: number;
/** 砍价价格 */
bargainPrice: number;
/** 状态 */
status: number;
/** 订单编号 */
orderId: number;
/** 结束时间 */
endTime: Date;
}
}
/** 查询砍价记录列表 */
export function getBargainRecordPage(params: PageParam) {
return requestClient.get<PageResult<MallBargainRecordApi.BargainRecord>>(
'/promotion/bargain-record/page',
{ params },
);
}

View File

@@ -0,0 +1,111 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { requestClient } from '#/api/request';
export namespace MallCombinationActivityApi {
/** 拼团活动所需属性 */
export interface CombinationProduct {
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SKU 编号 */
skuId: number;
/** 拼团价格 */
combinationPrice: number;
}
/** 拼团活动 */
export interface CombinationActivity {
/** 活动编号 */
id?: number;
/** 活动名称 */
name?: string;
/** 商品 SPU 编号 */
spuId?: number;
/** 总限购数量 */
totalLimitCount?: number;
/** 单次限购数量 */
singleLimitCount?: number;
/** 开始时间 */
startTime?: Date;
/** 结束时间 */
endTime?: Date;
/** 用户数量 */
userSize?: number;
/** 总数量 */
totalCount?: number;
/** 成功数量 */
successCount?: number;
/** 订单用户数量 */
orderUserCount?: number;
/** 虚拟成团 */
virtualGroup?: number;
/** 状态 */
status?: number;
/** 限制时长 */
limitDuration?: number;
/** 拼团价格 */
combinationPrice?: number;
/** 商品列表 */
products: CombinationProduct[];
}
/** 扩展 SKU 配置 */
export type SkuExtension = {
/** 拼团活动配置 */
productConfig: CombinationProduct;
} & MallSpuApi.Sku;
/** 扩展 SPU 配置 */
export interface SpuExtension extends MallSpuApi.Spu {
/** SKU 列表 */
skus: SkuExtension[];
}
}
/** 查询拼团活动列表 */
export function getCombinationActivityPage(params: PageParam) {
return requestClient.get<
PageResult<MallCombinationActivityApi.CombinationActivity>
>('/promotion/combination-activity/page', { params });
}
/** 查询拼团活动详情 */
export function getCombinationActivity(id: number) {
return requestClient.get<MallCombinationActivityApi.CombinationActivity>(
`/promotion/combination-activity/get?id=${id}`,
);
}
/** 获得拼团活动列表,基于活动编号数组 */
export function getCombinationActivityListByIds(ids: number[]) {
return requestClient.get<MallCombinationActivityApi.CombinationActivity[]>(
`/promotion/combination-activity/list-by-ids?ids=${ids}`,
);
}
/** 新增拼团活动 */
export function createCombinationActivity(
data: MallCombinationActivityApi.CombinationActivity,
) {
return requestClient.post('/promotion/combination-activity/create', data);
}
/** 修改拼团活动 */
export function updateCombinationActivity(
data: MallCombinationActivityApi.CombinationActivity,
) {
return requestClient.put('/promotion/combination-activity/update', data);
}
/** 关闭拼团活动 */
export function closeCombinationActivity(id: number) {
return requestClient.put(`/promotion/combination-activity/close?id=${id}`);
}
/** 删除拼团活动 */
export function deleteCombinationActivity(id: number) {
return requestClient.delete(
`/promotion/combination-activity/delete?id=${id}`,
);
}

View File

@@ -0,0 +1,61 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallCombinationRecordApi {
/** 拼团记录 */
export interface CombinationRecord {
/** 拼团记录编号 */
id: number;
/** 拼团活动编号 */
activityId: number;
/** 用户昵称 */
nickname: string;
/** 用户头像 */
avatar: string;
/** 团长编号 */
headId: number;
/** 过期时间 */
expireTime: string;
/** 可参团人数 */
userSize: number;
/** 已参团人数 */
userCount: number;
/** 拼团状态 */
status: number;
/** 商品名字 */
spuName: string;
/** 商品图片 */
picUrl: string;
/** 是否虚拟成团 */
virtualGroup: boolean;
/** 开始时间 (订单付款后开始的时间) */
startTime: string;
/** 结束时间(成团时间/失败时间) */
endTime: string;
}
/** 拼团记录概要信息 */
export interface RecordSummary {
/** 待成团数量 */
pendingCount: number;
/** 已成团数量 */
successCount: number;
/** 已失败数量 */
failCount: number;
}
}
/** 查询拼团记录列表 */
export function getCombinationRecordPage(params: PageParam) {
return requestClient.get<
PageResult<MallCombinationRecordApi.CombinationRecord>
>('/promotion/combination-record/page', { params });
}
/** 获得拼团记录的概要信息 */
export function getCombinationRecordSummary() {
return requestClient.get<MallCombinationRecordApi.RecordSummary>(
'/promotion/combination-record/get-summary',
);
}

View File

@@ -0,0 +1,67 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallCouponApi {
/** 优惠券 */
export interface Coupon {
/** 优惠券编号 */
id: number;
/** 优惠券名称 */
name: string;
/** 优惠券状态 */
status: number;
/** 优惠券类型 */
type: number;
/** 优惠券金额 */
price: number;
/** 使用门槛 */
usePrice: number;
/** 商品范围 */
productScope: number;
/** 商品编号数组 */
productSpuIds: number[];
/** 有效期类型 */
validityType: number;
/** 固定日期-生效开始时间 */
validStartTime: Date;
/** 固定日期-生效结束时间 */
validEndTime: Date;
/** 领取日期-开始天数 */
fixedStartTerm: number;
/** 领取日期-结束天数 */
fixedEndTerm: number;
/** 每人限领个数 */
takeLimitCount: number;
/** 是否设置满多少金额可用 */
usePriceEnabled: boolean;
/** 商品分类编号数组 */
productCategoryIds: number[];
}
/** 发送优惠券 */
export interface SendCoupon {
/** 优惠券编号 */
couponId: number;
/** 用户编号数组 */
userIds: number[];
}
}
/** 删除优惠劵 */
export function deleteCoupon(id: number) {
return requestClient.delete(`/promotion/coupon/delete?id=${id}`);
}
/** 获得优惠劵分页 */
export function getCouponPage(params: PageParam) {
return requestClient.get<PageResult<MallCouponApi.Coupon>>(
'/promotion/coupon/page',
{ params },
);
}
/** 发送优惠券 */
export function sendCoupon(data: MallCouponApi.SendCoupon) {
return requestClient.post('/promotion/coupon/send', data);
}

View File

@@ -0,0 +1,112 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallCouponTemplateApi {
/** 优惠券模板 */
export interface CouponTemplate {
/** 模板编号 */
id: number;
/** 模板名称 */
name: string;
/** 状态 */
status: number;
/** 发放数量 */
totalCount: number;
/** 每人限领个数 */
takeLimitCount: number;
/** 领取方式 */
takeType: number;
/** 使用门槛 */
usePrice: number;
/** 商品范围 */
productScope: number;
/** 商品范围值 */
productScopeValues: number[];
/** 有效期类型 */
validityType: number;
/** 固定日期-生效开始时间 */
validStartTime: Date;
/** 固定日期-生效结束时间 */
validEndTime: Date;
/** 领取日期-开始天数 */
fixedStartTerm: number;
/** 领取日期-结束天数 */
fixedEndTerm: number;
/** 优惠类型 */
discountType: number;
/** 折扣百分比 */
discountPercent: number;
/** 优惠金额 */
discountPrice: number;
/** 折扣上限 */
discountLimitPrice: number;
/** 已领取数量 */
takeCount: number;
/** 已使用数量 */
useCount: number;
}
/** 优惠券模板状态更新 */
export interface StatusUpdate {
/** 模板编号 */
id: number;
/** 状态 */
status: 0 | 1;
}
}
/** 创建优惠劵模板 */
export function createCouponTemplate(
data: MallCouponTemplateApi.CouponTemplate,
) {
return requestClient.post('/promotion/coupon-template/create', data);
}
/** 更新优惠劵模板 */
export function updateCouponTemplate(
data: MallCouponTemplateApi.CouponTemplate,
) {
return requestClient.put('/promotion/coupon-template/update', data);
}
/** 更新优惠劵模板的状态 */
export function updateCouponTemplateStatus(id: number, status: 0 | 1) {
const data: MallCouponTemplateApi.StatusUpdate = { id, status };
return requestClient.put('/promotion/coupon-template/update-status', data);
}
/** 删除优惠劵模板 */
export function deleteCouponTemplate(id: number) {
return requestClient.delete(`/promotion/coupon-template/delete?id=${id}`);
}
/** 获得优惠劵模板 */
export function getCouponTemplate(id: number) {
return requestClient.get<MallCouponTemplateApi.CouponTemplate>(
`/promotion/coupon-template/get?id=${id}`,
);
}
/** 获得优惠劵模板分页 */
export function getCouponTemplatePage(params: PageParam) {
return requestClient.get<PageResult<MallCouponTemplateApi.CouponTemplate>>(
'/promotion/coupon-template/page',
{ params },
);
}
/** 获得优惠劵模板列表 */
export function getCouponTemplateList(ids: number[]) {
return requestClient.get<MallCouponTemplateApi.CouponTemplate[]>(
`/promotion/coupon-template/list?ids=${ids}`,
);
}
/** 导出优惠劵模板 Excel */
export function exportCouponTemplateExcel(params: PageParam) {
return requestClient.get('/promotion/coupon-template/export-excel', {
params,
responseType: 'blob',
});
}

View File

@@ -0,0 +1,91 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { requestClient } from '#/api/request';
export namespace MallDiscountActivityApi {
/** 限时折扣相关属性 */
export interface DiscountProduct {
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SKU 编号 */
skuId: number;
/** 折扣类型 */
discountType: number;
/** 折扣百分比 */
discountPercent: number;
/** 折扣价格 */
discountPrice: number;
}
/** 限时折扣活动 */
export interface DiscountActivity {
/** 活动编号 */
id?: number;
/** 商品 SPU 编号 */
spuId?: number;
/** 活动名称 */
name?: string;
/** 状态 */
status?: number;
/** 备注 */
remark?: string;
/** 开始时间 */
startTime?: Date;
/** 结束时间 */
endTime?: Date;
/** 商品列表 */
products?: DiscountProduct[];
}
/** 扩展 SKU 配置 */
export type SkuExtension = {
/** 限时折扣配置 */
productConfig: DiscountProduct;
} & MallSpuApi.Sku;
/** 扩展 SPU 配置 */
export interface SpuExtension extends MallSpuApi.Spu {
/** SKU 列表 */
skus: SkuExtension[];
}
}
/** 查询限时折扣活动列表 */
export function getDiscountActivityPage(params: PageParam) {
return requestClient.get<
PageResult<MallDiscountActivityApi.DiscountActivity>
>('/promotion/discount-activity/page', { params });
}
/** 查询限时折扣活动详情 */
export function getDiscountActivity(id: number) {
return requestClient.get<MallDiscountActivityApi.DiscountActivity>(
`/promotion/discount-activity/get?id=${id}`,
);
}
/** 新增限时折扣活动 */
export function createDiscountActivity(
data: MallDiscountActivityApi.DiscountActivity,
) {
return requestClient.post('/promotion/discount-activity/create', data);
}
/** 修改限时折扣活动 */
export function updateDiscountActivity(
data: MallDiscountActivityApi.DiscountActivity,
) {
return requestClient.put('/promotion/discount-activity/update', data);
}
/** 关闭限时折扣活动 */
export function closeDiscountActivity(id: number) {
return requestClient.put(`/promotion/discount-activity/close?id=${id}`);
}
/** 删除限时折扣活动 */
export function deleteDiscountActivity(id: number) {
return requestClient.delete(`/promotion/discount-activity/delete?id=${id}`);
}

View File

@@ -0,0 +1,61 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallDiyPageApi {
/** 装修页面 */
export interface DiyPage {
/** 页面编号 */
id?: number;
/** 模板编号 */
templateId?: number;
/** 页面名称 */
name: string;
/** 备注 */
remark: string;
/** 预览图片地址数组 */
previewPicUrls: string[];
/** 页面属性 */
property: string;
}
}
/** 查询装修页面列表 */
export function getDiyPagePage(params: PageParam) {
return requestClient.get<PageResult<MallDiyPageApi.DiyPage>>(
'/promotion/diy-page/page',
{ params },
);
}
/** 查询装修页面详情 */
export function getDiyPage(id: number) {
return requestClient.get<MallDiyPageApi.DiyPage>(
`/promotion/diy-page/get?id=${id}`,
);
}
/** 新增装修页面 */
export function createDiyPage(data: MallDiyPageApi.DiyPage) {
return requestClient.post('/promotion/diy-page/create', data);
}
/** 修改装修页面 */
export function updateDiyPage(data: MallDiyPageApi.DiyPage) {
return requestClient.put('/promotion/diy-page/update', data);
}
/** 删除装修页面 */
export function deleteDiyPage(id: number) {
return requestClient.delete(`/promotion/diy-page/delete?id=${id}`);
}
/** 获得装修页面属性 */
export function getDiyPageProperty(id: number) {
return requestClient.get<string>(`/promotion/diy-page/get-property?id=${id}`);
}
/** 更新装修页面属性 */
export function updateDiyPageProperty(data: MallDiyPageApi.DiyPage) {
return requestClient.put('/promotion/diy-page/update-property', data);
}

View File

@@ -0,0 +1,80 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallDiyPageApi } from './page';
import { requestClient } from '#/api/request';
export namespace MallDiyTemplateApi {
/** 装修模板 */
export interface DiyTemplate {
/** 模板编号 */
id?: number;
/** 模板名称 */
name: string;
/** 是否使用 */
used: boolean;
/** 使用时间 */
usedTime?: Date;
/** 备注 */
remark: string;
/** 预览图片地址数组 */
previewPicUrls: string[];
/** 模板属性 */
property: string;
}
/** 装修模板属性(包含页面列表) */
export interface DiyTemplateProperty extends DiyTemplate {
/** 页面列表 */
pages: MallDiyPageApi.DiyPage[];
}
}
/** 查询装修模板列表 */
export function getDiyTemplatePage(params: PageParam) {
return requestClient.get<PageResult<MallDiyTemplateApi.DiyTemplate>>(
'/promotion/diy-template/page',
{ params },
);
}
/** 查询装修模板详情 */
export function getDiyTemplate(id: number) {
return requestClient.get<MallDiyTemplateApi.DiyTemplate>(
`/promotion/diy-template/get?id=${id}`,
);
}
/** 新增装修模板 */
export function createDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) {
return requestClient.post('/promotion/diy-template/create', data);
}
/** 修改装修模板 */
export function updateDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) {
return requestClient.put('/promotion/diy-template/update', data);
}
/** 删除装修模板 */
export function deleteDiyTemplate(id: number) {
return requestClient.delete(`/promotion/diy-template/delete?id=${id}`);
}
/** 使用装修模板 */
export function useDiyTemplate(id: number) {
return requestClient.put(`/promotion/diy-template/use?id=${id}`);
}
/** 获得装修模板属性 */
export function getDiyTemplateProperty(id: number) {
return requestClient.get<MallDiyTemplateApi.DiyTemplateProperty>(
`/promotion/diy-template/get-property?id=${id}`,
);
}
/** 更新装修模板属性 */
export function updateDiyTemplateProperty(
data: MallDiyTemplateApi.DiyTemplate,
) {
return requestClient.put('/promotion/diy-template/update-property', data);
}

View File

@@ -0,0 +1,70 @@
import type { PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallKefuConversationApi {
/** 客服会话 */
export interface Conversation {
/** 编号 */
id: number;
/** 会话所属用户 */
userId: number;
/** 会话所属用户头像 */
userAvatar: string;
/** 会话所属用户昵称 */
userNickname: string;
/** 最后聊天时间 */
lastMessageTime: Date;
/** 最后聊天内容 */
lastMessageContent: string;
/** 最后发送的消息类型 */
lastMessageContentType: number;
/** 管理端置顶 */
adminPinned: boolean;
/** 用户是否可见 */
userDeleted: boolean;
/** 管理员是否可见 */
adminDeleted: boolean;
/** 管理员未读消息数 */
adminUnreadMessageCount: number;
/** 创建时间 */
createTime?: string;
}
/** 会话置顶请求 */
export interface ConversationPinnedUpdate {
/** 会话编号 */
id: number;
/** 是否置顶 */
pinned: boolean;
}
}
/** 获得客服会话列表 */
export function getConversationList() {
return requestClient.get<PageResult<MallKefuConversationApi.Conversation>>(
'/promotion/kefu-conversation/list',
);
}
/** 获得客服会话 */
export function getConversation(id: number) {
return requestClient.get<MallKefuConversationApi.Conversation>(
`/promotion/kefu-conversation/get?id=${id}`,
);
}
/** 客服会话置顶 */
export function updateConversationPinned(
data: MallKefuConversationApi.ConversationPinnedUpdate,
) {
return requestClient.put(
'/promotion/kefu-conversation/update-conversation-pinned',
data,
);
}
/** 删除客服会话 */
export function deleteConversation(id: number) {
return requestClient.delete(`/promotion/kefu-conversation/delete?id=${id}`);
}

View File

@@ -0,0 +1,67 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallKefuMessageApi {
/** 客服消息 */
export interface Message {
/** 编号 */
id: number;
/** 会话编号 */
conversationId: number;
/** 发送人编号 */
senderId: number;
/** 发送人头像 */
senderAvatar: string;
/** 发送人类型 */
senderType: number;
/** 接收人编号 */
receiverId: number;
/** 接收人类型 */
receiverType: number;
/** 消息类型 */
contentType: number;
/** 消息内容 */
content: string;
/** 是否已读 */
readStatus: boolean;
/** 创建时间 */
createTime: Date;
}
/** 发送消息请求 */
export interface MessageSend {
/** 会话编号 */
conversationId: number;
/** 消息类型 */
contentType: number;
/** 消息内容 */
content: string;
}
/** 消息列表查询参数 */
export interface MessageQuery extends PageParam {
/** 会话编号 */
conversationId: number;
}
}
/** 发送客服消息 */
export function sendKeFuMessage(data: MallKefuMessageApi.MessageSend) {
return requestClient.post('/promotion/kefu-message/send', data);
}
/** 更新客服消息已读状态 */
export function updateKeFuMessageReadStatus(conversationId: number) {
return requestClient.put(
`/promotion/kefu-message/update-read-status?conversationId=${conversationId}`,
);
}
/** 获得消息列表(流式加载) */
export function getKeFuMessageList(params: MallKefuMessageApi.MessageQuery) {
return requestClient.get<PageResult<MallKefuMessageApi.Message>>(
'/promotion/kefu-message/list',
{ params },
);
}

View File

@@ -0,0 +1,127 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { requestClient } from '#/api/request';
export namespace MallPointActivityApi {
/** 积分商城商品 */
export interface PointProduct {
/** 积分商城商品编号 */
id?: number;
/** 积分商城活动 id */
activityId?: number;
/** 商品 SPU 编号 */
spuId?: number;
/** 商品 SKU 编号 */
skuId: number;
/** 可兑换数量 */
count: number;
/** 兑换积分 */
point: number;
/** 兑换金额,单位:分 */
price: number;
/** 积分商城商品库存 */
stock: number;
/** 积分商城商品状态 */
activityStatus?: number;
}
/** 积分商城活动 */
export interface PointActivity {
/** 积分商城活动编号 */
id: number;
/** 积分商城活动商品 */
spuId: number;
/** 活动状态 */
status: number;
/** 积分商城活动库存 */
stock: number;
/** 积分商城活动总库存 */
totalStock: number;
/** 备注 */
remark?: string;
/** 排序 */
sort: number;
/** 创建时间 */
createTime: string;
/** 积分商城商品 */
products: PointProduct[];
/** 商品名称 */
spuName: string;
/** 商品主图 */
picUrl: string;
/** 商品市场价,单位:分 */
marketPrice: number;
/** 兑换积分 */
point: number;
/** 兑换金额,单位:分 */
price: number;
}
/** 扩展 SKU 配置 */
export type SkuExtension = {
/** 积分商城商品配置 */
productConfig: PointProduct;
} & MallSpuApi.Sku;
/** 扩展 SPU 配置 */
export interface SpuExtension extends MallSpuApi.Spu {
/** SKU 列表 */
skus: SkuExtension[];
}
/** 扩展 SPU 配置(带积分信息) */
export interface SpuExtensionWithPoint extends MallSpuApi.Spu {
/** 积分商城活动库存 */
pointStock: number;
/** 积分商城活动总库存 */
pointTotalStock: number;
/** 兑换积分 */
point: number;
/** 兑换金额,单位:分 */
pointPrice: number;
}
}
/** 查询积分商城活动分页 */
export function getPointActivityPage(params: PageParam) {
return requestClient.get<PageResult<MallPointActivityApi.PointActivity>>(
'/promotion/point-activity/page',
{ params },
);
}
/** 查询积分商城活动详情 */
export function getPointActivity(id: number) {
return requestClient.get<MallPointActivityApi.PointActivity>(
`/promotion/point-activity/get?id=${id}`,
);
}
/** 查询积分商城活动列表,基于活动编号数组 */
export function getPointActivityListByIds(ids: number[]) {
return requestClient.get<MallPointActivityApi.PointActivity[]>(
`/promotion/point-activity/list-by-ids?ids=${ids}`,
);
}
/** 新增积分商城活动 */
export function createPointActivity(data: MallPointActivityApi.PointActivity) {
return requestClient.post('/promotion/point-activity/create', data);
}
/** 修改积分商城活动 */
export function updatePointActivity(data: MallPointActivityApi.PointActivity) {
return requestClient.put('/promotion/point-activity/update', data);
}
/** 删除积分商城活动 */
export function deletePointActivity(id: number) {
return requestClient.delete(`/promotion/point-activity/delete?id=${id}`);
}
/** 关闭积分商城活动 */
export function closePointActivity(id: number) {
return requestClient.put(`/promotion/point-activity/close?id=${id}`);
}

View File

@@ -0,0 +1,88 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallRewardActivityApi {
/** 优惠规则 */
export interface RewardRule {
/** 满足金额 */
limit?: number;
/** 优惠金额 */
discountPrice?: number;
/** 是否包邮 */
freeDelivery?: boolean;
/** 赠送积分 */
point: number;
/** 赠送优惠券数量 */
giveCouponTemplateCounts?: {
[key: number]: number;
};
}
/** 满减送活动 */
export interface RewardActivity {
/** 活动编号 */
id?: number;
/** 活动名称 */
name?: string;
/** 开始时间 */
startTime?: Date;
/** 结束时间 */
endTime?: Date;
/** 开始和结束时间(仅前端使用) */
startAndEndTime?: Date[];
/** 备注 */
remark?: string;
/** 条件类型 */
conditionType?: number;
/** 商品范围 */
productScope?: number;
/** 优惠规则列表 */
rules: RewardRule[];
/** 商品范围值(仅表单使用):值为品类编号列表、商品编号列表 */
productScopeValues?: number[];
/** 商品分类编号列表(仅表单使用) */
productCategoryIds?: number[];
/** 商品 SPU 编号列表(仅表单使用) */
productSpuIds?: number[];
}
}
/** 新增满减送活动 */
export function createRewardActivity(
data: MallRewardActivityApi.RewardActivity,
) {
return requestClient.post('/promotion/reward-activity/create', data);
}
/** 更新满减送活动 */
export function updateRewardActivity(
data: MallRewardActivityApi.RewardActivity,
) {
return requestClient.put('/promotion/reward-activity/update', data);
}
/** 查询满减送活动列表 */
export function getRewardActivityPage(params: PageParam) {
return requestClient.get<PageResult<MallRewardActivityApi.RewardActivity>>(
'/promotion/reward-activity/page',
{ params },
);
}
/** 查询满减送活动详情 */
export function getReward(id: number) {
return requestClient.get<MallRewardActivityApi.RewardActivity>(
`/promotion/reward-activity/get?id=${id}`,
);
}
/** 删除满减送活动 */
export function deleteRewardActivity(id: number) {
return requestClient.delete(`/promotion/reward-activity/delete?id=${id}`);
}
/** 关闭满减送活动 */
export function closeRewardActivity(id: number) {
return requestClient.put(`/promotion/reward-activity/close?id=${id}`);
}

View File

@@ -0,0 +1,117 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { requestClient } from '#/api/request';
export namespace MallSeckillActivityApi {
/** 秒杀商品 */
export interface SeckillProduct {
/** 商品 SKU 编号 */
skuId: number;
/** 商品 SPU 编号 */
spuId: number;
/** 秒杀价格 */
seckillPrice: number;
/** 秒杀库存 */
stock: number;
}
/** 秒杀活动 */
export interface SeckillActivity {
/** 活动编号 */
id?: number;
/** 商品 SPU 编号 */
spuId?: number;
/** 活动名称 */
name?: string;
/** 活动状态 */
status?: number;
/** 备注 */
remark?: string;
/** 开始时间 */
startTime?: Date;
/** 结束时间 */
endTime?: Date;
/** 排序 */
sort?: number;
/** 配置编号 */
configIds?: string;
/** 订单数量 */
orderCount?: number;
/** 用户数量 */
userCount?: number;
/** 总金额 */
totalPrice?: number;
/** 总限购数量 */
totalLimitCount?: number;
/** 单次限购数量 */
singleLimitCount?: number;
/** 秒杀库存 */
stock?: number;
/** 秒杀总库存 */
totalStock?: number;
/** 秒杀价格 */
seckillPrice?: number;
/** 秒杀商品列表 */
products?: SeckillProduct[];
}
/** 扩展 SKU 配置 */
export type SkuExtension = {
/** 秒杀商品配置 */
productConfig: SeckillProduct;
} & MallSpuApi.Sku;
/** 扩展 SPU 配置 */
export interface SpuExtension extends MallSpuApi.Spu {
/** SKU 列表 */
skus: SkuExtension[];
}
}
/** 查询秒杀活动列表 */
export function getSeckillActivityPage(params: PageParam) {
return requestClient.get<PageResult<MallSeckillActivityApi.SeckillActivity>>(
'/promotion/seckill-activity/page',
{ params },
);
}
/** 查询秒杀活动列表,基于活动编号数组 */
export function getSeckillActivityListByIds(ids: number[]) {
return requestClient.get<MallSeckillActivityApi.SeckillActivity[]>(
`/promotion/seckill-activity/list-by-ids?ids=${ids}`,
);
}
/** 查询秒杀活动详情 */
export function getSeckillActivity(id: number) {
return requestClient.get<MallSeckillActivityApi.SeckillActivity>(
`/promotion/seckill-activity/get?id=${id}`,
);
}
/** 新增秒杀活动 */
export function createSeckillActivity(
data: MallSeckillActivityApi.SeckillActivity,
) {
return requestClient.post('/promotion/seckill-activity/create', data);
}
/** 修改秒杀活动 */
export function updateSeckillActivity(
data: MallSeckillActivityApi.SeckillActivity,
) {
return requestClient.put('/promotion/seckill-activity/update', data);
}
/** 关闭秒杀活动 */
export function closeSeckillActivity(id: number) {
return requestClient.put(`/promotion/seckill-activity/close?id=${id}`);
}
/** 删除秒杀活动 */
export function deleteSeckillActivity(id: number) {
return requestClient.delete(`/promotion/seckill-activity/delete?id=${id}`);
}

View File

@@ -0,0 +1,74 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallSeckillConfigApi {
/** 秒杀时段 */
export interface SeckillConfig {
/** 编号 */
id: number;
/** 秒杀时段名称 */
name: string;
/** 开始时间点 */
startTime: string;
/** 结束时间点 */
endTime: string;
/** 秒杀轮播图 */
sliderPicUrls: string[];
/** 活动状态 */
status: number;
}
/** 时段配置状态更新 */
export interface StatusUpdate {
/** 编号 */
id: number;
/** 状态 */
status: number;
}
}
/** 查询秒杀时段分页 */
export function getSeckillConfigPage(params: PageParam) {
return requestClient.get<PageResult<MallSeckillConfigApi.SeckillConfig>>(
'/promotion/seckill-config/page',
{ params },
);
}
/** 查询秒杀时段列表 */
export function getSimpleSeckillConfigList() {
return requestClient.get<MallSeckillConfigApi.SeckillConfig[]>(
'/promotion/seckill-config/list',
);
}
/** 查询秒杀时段详情 */
export function getSeckillConfig(id: number) {
return requestClient.get<MallSeckillConfigApi.SeckillConfig>(
`/promotion/seckill-config/get?id=${id}`,
);
}
/** 新增秒杀时段 */
export function createSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) {
return requestClient.post('/promotion/seckill-config/create', data);
}
/** 修改秒杀时段 */
export function updateSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) {
return requestClient.put('/promotion/seckill-config/update', data);
}
/** 删除秒杀时段 */
export function deleteSeckillConfig(id: number) {
return requestClient.delete(`/promotion/seckill-config/delete?id=${id}`);
}
/** 修改时段配置状态 */
export function updateSeckillConfigStatus(id: number, status: number) {
return requestClient.put('/promotion/seckill-config/update-status', {
id,
status,
});
}

View File

@@ -0,0 +1,5 @@
/** 数据对照 Response VO */
export interface MallDataComparisonRespVO<T> {
value: T;
reference: T;
}

View File

@@ -0,0 +1,131 @@
import type { MallDataComparisonRespVO } from './common';
import { formatDate } from '@vben/utils';
import { requestClient } from '#/api/request';
export namespace MallMemberStatisticsApi {
/** 会员分析 Request VO */
export interface AnalyseReq {
times: Date[];
}
/** 会员分析对照数据 Response VO */
export interface AnalyseComparison {
registerUserCount: number;
visitUserCount: number;
rechargeUserCount: number;
}
/** 会员分析 Response VO */
export interface Analyse {
visitUserCount: number;
orderUserCount: number;
payUserCount: number;
atv: number;
comparison: MallDataComparisonRespVO<AnalyseComparison>;
}
/** 会员地区统计 Response VO */
export interface AreaStatistics {
areaId: number;
areaName: string;
userCount: number;
orderCreateUserCount: number;
orderPayUserCount: number;
orderPayPrice: number;
}
/** 会员性别统计 Response VO */
export interface SexStatistics {
sex: number;
userCount: number;
}
/** 会员统计 Response VO */
export interface Summary {
userCount: number;
rechargeUserCount: number;
rechargePrice: number;
expensePrice: number;
}
/** 会员终端统计 Response VO */
export interface TerminalStatistics {
terminal: number;
userCount: number;
}
/** 会员数量统计 Response VO */
export interface Count {
/** 用户访问量 */
visitUserCount: string;
/** 注册用户数量 */
registerUserCount: number;
}
/** 会员注册数量 Response VO */
export interface RegisterCount {
date: string;
count: number;
}
}
/** 查询会员统计 */
export function getMemberSummary() {
return requestClient.get<MallMemberStatisticsApi.Summary>(
'/statistics/member/summary',
);
}
/** 查询会员分析数据 */
export function getMemberAnalyse(params: MallMemberStatisticsApi.AnalyseReq) {
return requestClient.get<MallMemberStatisticsApi.Analyse>(
'/statistics/member/analyse',
{
params: {
times: [formatDate(params.times[0]), formatDate(params.times[1])],
},
},
);
}
/** 按照省份,查询会员统计列表 */
export function getMemberAreaStatisticsList() {
return requestClient.get<MallMemberStatisticsApi.AreaStatistics[]>(
'/statistics/member/area-statistics-list',
);
}
/** 按照性别,查询会员统计列表 */
export function getMemberSexStatisticsList() {
return requestClient.get<MallMemberStatisticsApi.SexStatistics[]>(
'/statistics/member/sex-statistics-list',
);
}
/** 按照终端,查询会员统计列表 */
export function getMemberTerminalStatisticsList() {
return requestClient.get<MallMemberStatisticsApi.TerminalStatistics[]>(
'/statistics/member/terminal-statistics-list',
);
}
/** 获得用户数量量对照 */
export function getUserCountComparison() {
return requestClient.get<
MallDataComparisonRespVO<MallMemberStatisticsApi.Count>
>('/statistics/member/user-count-comparison');
}
/** 获得会员注册数量列表 */
export function getMemberRegisterCountList(beginTime: Date, endTime: Date) {
return requestClient.get<MallMemberStatisticsApi.RegisterCount[]>(
'/statistics/member/register-count-list',
{
params: {
times: [formatDate(beginTime), formatDate(endTime)],
},
},
);
}

View File

@@ -0,0 +1,16 @@
import { requestClient } from '#/api/request';
export namespace MallPayStatisticsApi {
/** 支付统计 */
export interface PaySummaryRespVO {
/** 充值金额,单位分 */
rechargePrice: number;
}
}
/** 获取钱包充值金额 */
export function getWalletRechargePrice() {
return requestClient.get<MallPayStatisticsApi.PaySummaryRespVO>(
'/statistics/pay/summary',
);
}

View File

@@ -0,0 +1,68 @@
import type { PageParam, PageResult } from '@vben/request';
import type { MallDataComparisonRespVO } from './common';
import { requestClient } from '#/api/request';
export namespace MallProductStatisticsApi {
/** 商品统计数据 */
export interface ProductStatistics {
/** 编号 */
id: number;
/** 统计日期 */
day: string;
/** 商品 SPU 编号 */
spuId: number;
/** 商品 SPU 名称 */
spuName: string;
/** 商品 SPU 图片 */
spuPicUrl: string;
/** 浏览次数 */
browseCount: number;
/** 浏览人数 */
browseUserCount: number;
/** 收藏次数 */
favoriteCount: number;
/** 加购次数 */
cartCount: number;
/** 下单次数 */
orderCount: number;
/** 支付次数 */
orderPayCount: number;
/** 支付金额 */
orderPayPrice: number;
/** 售后次数 */
afterSaleCount: number;
/** 退款金额 */
afterSaleRefundPrice: number;
/** 浏览转化率 */
browseConvertPercent: number;
}
}
/** 获得商品统计分析 */
export function getProductStatisticsAnalyse(params: PageParam) {
return requestClient.get<
MallDataComparisonRespVO<MallProductStatisticsApi.ProductStatistics>
>('/statistics/product/analyse', { params });
}
/** 获得商品状况明细 */
export function getProductStatisticsList(params: PageParam) {
return requestClient.get<MallProductStatisticsApi.ProductStatistics[]>(
'/statistics/product/list',
{ params },
);
}
/** 导出获得商品状况明细 Excel */
export function exportProductStatisticsExcel(params: PageParam) {
return requestClient.download('/statistics/product/export-excel', { params });
}
/** 获得商品排行榜分页 */
export function getProductStatisticsRankPage(params: PageParam) {
return requestClient.get<
PageResult<MallProductStatisticsApi.ProductStatistics>
>('/statistics/product/rank-page', { params });
}

View File

@@ -0,0 +1,135 @@
import type { MallDataComparisonRespVO } from './common';
import { formatDate } from '@vben/utils';
import { requestClient } from '#/api/request';
export namespace MallTradeStatisticsApi {
/** 交易统计 Response VO */
export interface TradeSummary {
yesterdayOrderCount: number;
monthOrderCount: number;
yesterdayPayPrice: number;
monthPayPrice: number;
}
/** 交易状况 Request VO */
export interface TradeTrendReq {
times: [Date, Date];
}
/** 交易状况统计 Response VO */
export interface TradeTrendSummary {
time: string;
turnoverPrice: number;
orderPayPrice: number;
rechargePrice: number;
expensePrice: number;
walletPayPrice: number;
brokerageSettlementPrice: number;
afterSaleRefundPrice: number;
}
/** 交易订单数量 Response VO */
export interface TradeOrderCount {
/** 待发货 */
undelivered?: number;
/** 待核销 */
pickUp?: number;
/** 退款中 */
afterSaleApply?: number;
/** 提现待审核 */
auditingWithdraw?: number;
}
/** 交易订单统计 Response VO */
export interface TradeOrderSummary {
/** 支付订单商品数 */
orderPayCount?: number;
/** 总支付金额,单位:分 */
orderPayPrice?: number;
}
/** 订单量趋势统计 Response VO */
export interface TradeOrderTrend {
/** 日期 */
date: string;
/** 订单数量 */
orderPayCount: number;
/** 订单支付金额 */
orderPayPrice: number;
}
}
/** 时间参数需要格式化, 确保接口能识别 */
const formatDateParam = (params: MallTradeStatisticsApi.TradeTrendReq) => {
return {
times: [formatDate(params.times[0]), formatDate(params.times[1])],
} as MallTradeStatisticsApi.TradeTrendReq;
};
/** 查询交易统计 */
export function getTradeStatisticsSummary() {
return requestClient.get<
MallDataComparisonRespVO<MallTradeStatisticsApi.TradeSummary>
>('/statistics/trade/summary');
}
/** 获得交易状况统计 */
export function getTradeStatisticsAnalyse(
params: MallTradeStatisticsApi.TradeTrendReq,
) {
return requestClient.get<
MallDataComparisonRespVO<MallTradeStatisticsApi.TradeTrendSummary>
>('/statistics/trade/analyse', { params: formatDateParam(params) });
}
/** 获得交易状况明细 */
export function getTradeStatisticsList(
params: MallTradeStatisticsApi.TradeTrendReq,
) {
return requestClient.get<MallTradeStatisticsApi.TradeTrendSummary[]>(
'/statistics/trade/list',
{ params: formatDateParam(params) },
);
}
/** 导出交易状况明细 */
export function exportTradeStatisticsExcel(
params: MallTradeStatisticsApi.TradeTrendReq,
) {
return requestClient.download('/statistics/trade/export-excel', {
params: formatDateParam(params),
});
}
/** 获得交易订单数量 */
export function getOrderCount() {
return requestClient.get<MallTradeStatisticsApi.TradeOrderCount>(
'/statistics/trade/order-count',
);
}
/** 获得交易订单数量对照 */
export function getOrderComparison() {
return requestClient.get<
MallDataComparisonRespVO<MallTradeStatisticsApi.TradeOrderSummary>
>('/statistics/trade/order-comparison');
}
/** 获得订单量趋势统计 */
export function getOrderCountTrendComparison(
type: number,
beginTime: Date,
endTime: Date,
) {
return requestClient.get<
MallDataComparisonRespVO<MallTradeStatisticsApi.TradeOrderTrend>[]
>('/statistics/trade/order-count-trend', {
params: {
type,
beginTime: formatDate(beginTime),
endTime: formatDate(endTime),
},
});
}

View File

@@ -0,0 +1,127 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallAfterSaleApi {
/** 商品属性 */
export interface ProductProperty {
/** 属性的编号 */
propertyId?: null | number;
/** 属性的名称 */
propertyName?: string;
/** 属性值的编号 */
valueId?: null | number;
/** 属性值的名称 */
valueName?: string;
}
/** 交易售后 */
export interface AfterSale {
/** 售后编号,主键自增 */
id?: null | number;
/** 售后单号 */
no?: string;
/** 退款状态 */
status?: null | number;
/** 售后方式 */
way?: null | number;
/** 售后类型 */
type?: null | number;
/** 用户编号 */
userId?: null | number;
/** 申请原因 */
applyReason?: string;
/** 补充描述 */
applyDescription?: string;
/** 补充凭证图片 */
applyPicUrls?: string[];
/** 交易订单编号 */
orderId?: null | number;
/** 订单流水号 */
orderNo?: string;
/** 交易订单项编号 */
orderItemId?: null | number;
/** 商品 SPU 编号 */
spuId?: null | number;
/** 商品 SPU 名称 */
spuName?: string;
/** 商品 SKU 编号 */
skuId?: null | number;
/** 属性数组 */
properties?: ProductProperty[];
/** 商品图片 */
picUrl?: string;
/** 退货商品数量 */
count?: null | number;
/** 审批时间 */
auditTime?: Date;
/** 审批人 */
auditUserId?: null | number;
/** 审批备注 */
auditReason?: string;
/** 退款金额,单位:分 */
refundPrice?: null | number;
/** 支付退款编号 */
payRefundId?: null | number;
/** 退款时间 */
refundTime?: Date;
/** 退货物流公司编号 */
logisticsId?: null | number;
/** 退货物流单号 */
logisticsNo?: string;
/** 退货时间 */
deliveryTime?: Date;
/** 收货时间 */
receiveTime?: Date;
/** 收货备注 */
receiveReason?: string;
}
/** 拒绝售后请求 */
export interface DisagreeRequest {
/** 售后编号 */
id: number;
/** 拒绝原因 */
reason: string;
}
}
/** 获得交易售后分页 */
export function getAfterSalePage(params: PageParam) {
return requestClient.get<PageResult<MallAfterSaleApi.AfterSale>>(
'/trade/after-sale/page',
{ params },
);
}
/** 获得交易售后详情 */
export function getAfterSale(id: number) {
return requestClient.get<MallAfterSaleApi.AfterSale>(
`/trade/after-sale/get-detail?id=${id}`,
);
}
/** 同意售后 */
export function agree(id: number) {
return requestClient.put(`/trade/after-sale/agree?id=${id}`);
}
/** 拒绝售后 */
export function disagree(data: MallAfterSaleApi.DisagreeRequest) {
return requestClient.put('/trade/after-sale/disagree', data);
}
/** 确认收货 */
export function receive(id: number) {
return requestClient.put(`/trade/after-sale/receive?id=${id}`);
}
/** 拒绝收货 */
export function refuse(id: number) {
return requestClient.put(`/trade/after-sale/refuse?id=${id}`);
}
/** 确认退款 */
export function refund(id: number) {
return requestClient.put(`/trade/after-sale/refund?id=${id}`);
}

View File

@@ -0,0 +1,46 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBrokerageRecordApi {
/** 佣金记录 */
export interface BrokerageRecord {
/** 编号 */
id: number;
/** 用户编号 */
userId: number;
/** 用户昵称 */
userNickname: string;
/** 用户头像 */
userAvatar: string;
/** 佣金金额,单位:分 */
price: number;
/** 佣金类型 */
type: number;
/** 关联订单编号 */
orderId: number;
/** 关联订单号 */
orderNo: string;
/** 创建时间 */
createTime: Date;
/** 状态 */
status: number;
/** 结算时间 */
settlementTime: Date;
}
}
/** 查询佣金记录列表 */
export function getBrokerageRecordPage(params: PageParam) {
return requestClient.get<PageResult<MallBrokerageRecordApi.BrokerageRecord>>(
'/trade/brokerage-record/page',
{ params },
);
}
/** 查询佣金记录详情 */
export function getBrokerageRecord(id: number) {
return requestClient.get<MallBrokerageRecordApi.BrokerageRecord>(
`/trade/brokerage-record/get?id=${id}`,
);
}

View File

@@ -0,0 +1,97 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBrokerageUserApi {
/** 分销用户 */
export interface BrokerageUser {
/** 编号 */
id: number;
/** 推广员编号 */
bindUserId: number;
/** 推广员绑定时间 */
bindUserTime: Date;
/** 是否启用分销 */
brokerageEnabled: boolean;
/** 分销资格时间 */
brokerageTime: Date;
/** 可提现金额,单位:分 */
price: number;
/** 冻结金额,单位:分 */
frozenPrice: number;
/** 用户昵称 */
nickname: string;
/** 用户头像 */
avatar: string;
}
/** 创建分销用户请求 */
export interface CreateRequest {
/** 用户编号 */
userId: number;
}
/** 修改推广员请求 */
export interface UpdateBindUserRequest {
/** 用户编号 */
userId: number;
/** 推广员编号 */
bindUserId: number;
}
/** 清除推广员请求 */
export interface ClearBindUserRequest {
/** 用户编号 */
userId: number;
}
/** 修改推广资格请求 */
export interface UpdateBrokerageEnabledRequest {
/** 用户编号 */
userId: number;
/** 是否启用分销 */
brokerageEnabled: boolean;
}
}
/** 创建分销用户 */
export function createBrokerageUser(data: MallBrokerageUserApi.CreateRequest) {
return requestClient.post('/trade/brokerage-user/create', data);
}
/** 查询分销用户列表 */
export function getBrokerageUserPage(params: PageParam) {
return requestClient.get<PageResult<MallBrokerageUserApi.BrokerageUser>>(
'/trade/brokerage-user/page',
{ params },
);
}
/** 查询分销用户详情 */
export function getBrokerageUser(id: number) {
return requestClient.get<MallBrokerageUserApi.BrokerageUser>(
`/trade/brokerage-user/get?id=${id}`,
);
}
/** 修改推广员 */
export function updateBindUser(
data: MallBrokerageUserApi.UpdateBindUserRequest,
) {
return requestClient.put('/trade/brokerage-user/update-bind-user', data);
}
/** 清除推广员 */
export function clearBindUser(data: MallBrokerageUserApi.ClearBindUserRequest) {
return requestClient.put('/trade/brokerage-user/clear-bind-user', data);
}
/** 修改推广资格 */
export function updateBrokerageEnabled(
data: MallBrokerageUserApi.UpdateBrokerageEnabledRequest,
) {
return requestClient.put(
'/trade/brokerage-user/update-brokerage-enable',
data,
);
}

View File

@@ -0,0 +1,81 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallBrokerageWithdrawApi {
/** 佣金提现 */
export interface BrokerageWithdraw {
/** 编号 */
id: number;
/** 用户编号 */
userId: number;
/** 提现金额,单位:分 */
price: number;
/** 手续费,单位:分 */
feePrice: number;
/** 总金额,单位:分 */
totalPrice: number;
/** 提现类型 */
type: number;
/** 用户名称 */
userName: string;
/** 用户账号 */
userAccount: string;
/** 银行名称 */
bankName: string;
/** 银行地址 */
bankAddress: string;
/** 收款码地址 */
qrCodeUrl: string;
/** 状态 */
status: number;
/** 审核备注 */
auditReason: string;
/** 审核时间 */
auditTime: Date;
/** 备注 */
remark: string;
/** 支付转账编号 */
payTransferId?: number;
/** 转账渠道编码 */
transferChannelCode?: string;
/** 转账时间 */
transferTime?: Date;
/** 转账错误信息 */
transferErrorMsg?: string;
}
/** 驳回申请请求 */
export interface RejectRequest {
/** 编号 */
id: number;
/** 驳回原因 */
auditReason: string;
}
}
/** 查询佣金提现列表 */
export function getBrokerageWithdrawPage(params: PageParam) {
return requestClient.get<
PageResult<MallBrokerageWithdrawApi.BrokerageWithdraw>
>('/trade/brokerage-withdraw/page', { params });
}
/** 查询佣金提现详情 */
export function getBrokerageWithdraw(id: number) {
return requestClient.get<MallBrokerageWithdrawApi.BrokerageWithdraw>(
`/trade/brokerage-withdraw/get?id=${id}`,
);
}
/** 佣金提现 - 通过申请 */
export function approveBrokerageWithdraw(id: number) {
return requestClient.put(`/trade/brokerage-withdraw/approve?id=${id}`);
}
/** 审核佣金提现 - 驳回申请 */
export function rejectBrokerageWithdraw(
data: MallBrokerageWithdrawApi.RejectRequest,
) {
return requestClient.put('/trade/brokerage-withdraw/reject', data);
}

View File

@@ -0,0 +1,33 @@
import { requestClient } from '#/api/request';
export namespace MallTradeConfigApi {
/** 交易中心配置 */
export interface Config {
id?: number;
afterSaleRefundReasons?: string[];
afterSaleReturnReasons?: string[];
deliveryExpressFreeEnabled?: boolean;
deliveryExpressFreePrice?: number;
deliveryPickUpEnabled?: boolean;
brokerageEnabled?: boolean;
brokerageEnabledCondition?: number;
brokerageBindMode?: number;
brokeragePosterUrls?: string;
brokerageFirstPercent?: number;
brokerageSecondPercent?: number;
brokerageWithdrawMinPrice?: number;
brokerageFrozenDays?: number;
brokerageWithdrawTypes?: string;
tencentLbsKey?: string;
}
}
/** 查询交易中心配置详情 */
export function getTradeConfig() {
return requestClient.get<MallTradeConfigApi.Config>('/trade/config/get');
}
/** 保存交易中心配置 */
export function saveTradeConfig(data: MallTradeConfigApi.Config) {
return requestClient.put('/trade/config/save', data);
}

View File

@@ -0,0 +1,79 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallDeliveryExpressApi {
/** 快递公司 */
export interface DeliveryExpress {
/** 编号 */
id: number;
/** 快递公司编码 */
code: string;
/** 快递公司名称 */
name: string;
/** 快递公司 logo */
logo: string;
/** 排序 */
sort: number;
/** 状态 */
status: number;
}
/** 快递公司精简信息 */
export interface SimpleDeliveryExpress {
/** 编号 */
id: number;
/** 快递公司编码 */
code: string;
/** 快递公司名称 */
name: string;
}
}
/** 查询快递公司列表 */
export function getDeliveryExpressPage(params: PageParam) {
return requestClient.get<PageResult<MallDeliveryExpressApi.DeliveryExpress>>(
'/trade/delivery/express/page',
{ params },
);
}
/** 查询快递公司详情 */
export function getDeliveryExpress(id: number) {
return requestClient.get<MallDeliveryExpressApi.DeliveryExpress>(
`/trade/delivery/express/get?id=${id}`,
);
}
/** 获得快递公司精简信息列表 */
export function getSimpleDeliveryExpressList() {
return requestClient.get<MallDeliveryExpressApi.SimpleDeliveryExpress[]>(
'/trade/delivery/express/list-all-simple',
);
}
/** 新增快递公司 */
export function createDeliveryExpress(
data: MallDeliveryExpressApi.DeliveryExpress,
) {
return requestClient.post('/trade/delivery/express/create', data);
}
/** 修改快递公司 */
export function updateDeliveryExpress(
data: MallDeliveryExpressApi.DeliveryExpress,
) {
return requestClient.put('/trade/delivery/express/update', data);
}
/** 删除快递公司 */
export function deleteDeliveryExpress(id: number) {
return requestClient.delete(`/trade/delivery/express/delete?id=${id}`);
}
/** 导出快递公司 Excel */
export function exportDeliveryExpress(params: PageParam) {
return requestClient.download('/trade/delivery/express/export-excel', {
params,
});
}

View File

@@ -0,0 +1,95 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallDeliveryExpressTemplateApi {
/** 运费模板计费 */
export interface TemplateCharge {
/** 区域编号列表 */
areaIds: number[];
/** 首件数量 */
startCount: number;
/** 首件价格,单位:分 */
startPrice: number;
/** 续件数量 */
extraCount: number;
/** 续件价格,单位:分 */
extraPrice: number;
}
/** 运费模板包邮 */
export interface TemplateFree {
/** 区域编号列表 */
areaIds: number[];
/** 包邮件数 */
freeCount: number;
/** 包邮金额,单位:分 */
freePrice: number;
}
/** 快递运费模板 */
export interface ExpressTemplate {
/** 编号 */
id: number;
/** 模板名称 */
name: string;
/** 计费方式 */
chargeMode: number;
/** 排序 */
sort: number;
/** 计费区域列表 */
templateCharge: TemplateCharge[];
/** 包邮区域列表 */
templateFree: TemplateFree[];
}
/** 运费模板精简信息 */
export interface SimpleTemplate {
/** 编号 */
id: number;
/** 模板名称 */
name: string;
}
}
/** 查询快递运费模板列表 */
export function getDeliveryExpressTemplatePage(params: PageParam) {
return requestClient.get<
PageResult<MallDeliveryExpressTemplateApi.ExpressTemplate>
>('/trade/delivery/express-template/page', { params });
}
/** 查询快递运费模板详情 */
export function getDeliveryExpressTemplate(id: number) {
return requestClient.get<MallDeliveryExpressTemplateApi.ExpressTemplate>(
`/trade/delivery/express-template/get?id=${id}`,
);
}
/** 查询快递运费模板详情 */
export function getSimpleTemplateList() {
return requestClient.get<MallDeliveryExpressTemplateApi.SimpleTemplate[]>(
'/trade/delivery/express-template/list-all-simple',
);
}
/** 新增快递运费模板 */
export function createDeliveryExpressTemplate(
data: MallDeliveryExpressTemplateApi.ExpressTemplate,
) {
return requestClient.post('/trade/delivery/express-template/create', data);
}
/** 修改快递运费模板 */
export function updateDeliveryExpressTemplate(
data: MallDeliveryExpressTemplateApi.ExpressTemplate,
) {
return requestClient.put('/trade/delivery/express-template/update', data);
}
/** 删除快递运费模板 */
export function deleteDeliveryExpressTemplate(id: number) {
return requestClient.delete(
`/trade/delivery/express-template/delete?id=${id}`,
);
}

View File

@@ -0,0 +1,91 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallDeliveryPickUpStoreApi {
/** 自提门店 */
export interface PickUpStore {
/** 编号 */
id: number;
/** 门店名称 */
name: string;
/** 门店简介 */
introduction: string;
/** 联系电话 */
phone: string;
/** 区域编号 */
areaId: number;
/** 详细地址 */
detailAddress: string;
/** 门店 logo */
logo: string;
/** 营业开始时间 */
openingTime: string;
/** 营业结束时间 */
closingTime: string;
/** 纬度 */
latitude: number;
/** 经度 */
longitude: number;
/** 状态 */
status: number;
/** 绑定用户编号组数 */
verifyUserIds: number[];
}
/** 绑定自提店员请求 */
export interface BindStaffRequest {
/** 门店编号 */
storeId: number;
/** 用户编号列表 */
userIds: number[];
}
}
/** 查询自提门店列表 */
export function getDeliveryPickUpStorePage(params: PageParam) {
return requestClient.get<PageResult<MallDeliveryPickUpStoreApi.PickUpStore>>(
'/trade/delivery/pick-up-store/page',
{ params },
);
}
/** 查询自提门店详情 */
export function getDeliveryPickUpStore(id: number) {
return requestClient.get<MallDeliveryPickUpStoreApi.PickUpStore>(
`/trade/delivery/pick-up-store/get?id=${id}`,
);
}
/** 查询自提门店精简列表 */
export function getSimpleDeliveryPickUpStoreList() {
return requestClient.get<MallDeliveryPickUpStoreApi.PickUpStore[]>(
'/trade/delivery/pick-up-store/simple-list',
);
}
/** 新增自提门店 */
export function createDeliveryPickUpStore(
data: MallDeliveryPickUpStoreApi.PickUpStore,
) {
return requestClient.post('/trade/delivery/pick-up-store/create', data);
}
/** 修改自提门店 */
export function updateDeliveryPickUpStore(
data: MallDeliveryPickUpStoreApi.PickUpStore,
) {
return requestClient.put('/trade/delivery/pick-up-store/update', data);
}
/** 删除自提门店 */
export function deleteDeliveryPickUpStore(id: number) {
return requestClient.delete(`/trade/delivery/pick-up-store/delete?id=${id}`);
}
/** 绑定自提店员 */
export function bindStoreStaffId(
data: MallDeliveryPickUpStoreApi.BindStaffRequest,
) {
return requestClient.post('/trade/delivery/pick-up-store/bind', data);
}

View File

@@ -0,0 +1,298 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MallOrderApi {
/** 商品属性 */
export interface ProductProperty {
/** 属性的编号 */
propertyId?: null | number;
/** 属性的名称 */
propertyName?: string;
/** 属性值的编号 */
valueId?: null | number;
/** 属性值的名称 */
valueName?: string;
}
/** 订单项 */
export interface OrderItem {
/** 编号 */
id?: null | number;
/** 用户编号 */
userId?: null | number;
/** 订单编号 */
orderId?: null | number;
/** 商品 SPU 编号 */
spuId?: null | number;
/** 商品 SPU 名称 */
spuName?: string;
/** 商品 SKU 编号 */
skuId?: null | number;
/** 商品图片 */
picUrl?: string;
/** 购买数量 */
count?: null | number;
/** 商品原价(总) */
originalPrice?: null | number;
/** 商品原价(单) */
originalUnitPrice?: null | number;
/** 商品优惠(总) */
discountPrice?: null | number;
/** 商品实付金额(总) */
payPrice?: null | number;
/** 子订单分摊金额(总) */
orderPartPrice?: null | number;
/** 分摊后子订单实付金额(总) */
orderDividePrice?: null | number;
/** 售后状态 */
afterSaleStatus?: null | number;
/** 属性数组 */
properties?: ProductProperty[];
}
/** 订单日志 */
export interface OrderLog {
/** 日志内容 */
content?: string;
/** 创建时间 */
createTime?: Date;
/** 用户类型 */
userType?: number;
}
/** 订单 */
export interface Order {
/** 订单编号 */
id?: null | number;
/** 订单流水号 */
no?: string;
/** 下单时间 */
createTime?: Date | null;
/** 订单类型 */
type?: null | number;
/** 订单来源 */
terminal?: null | number;
/** 用户编号 */
userId?: null | number;
/** 用户 IP */
userIp?: string;
/** 用户备注 */
userRemark?: string;
/** 订单状态 */
status?: null | number;
/** 购买的商品数量 */
productCount?: null | number;
/** 订单完成时间 */
finishTime?: Date | null;
/** 订单取消时间 */
cancelTime?: Date | null;
/** 取消类型 */
cancelType?: null | number;
/** 商家备注 */
remark?: string;
/** 支付订单编号 */
payOrderId?: null | number;
/** 是否已支付 */
payStatus?: boolean;
/** 付款时间 */
payTime?: Date | null;
/** 支付渠道 */
payChannelCode?: string;
/** 商品原价(总) */
totalPrice?: null | number;
/** 订单优惠(总) */
discountPrice?: null | number;
/** 运费金额 */
deliveryPrice?: null | number;
/** 订单调价(总) */
adjustPrice?: null | number;
/** 应付金额(总) */
payPrice?: null | number;
/** 发货方式 */
deliveryType?: null | number;
/** 自提门店编号 */
pickUpStoreId?: number;
/** 自提核销码 */
pickUpVerifyCode?: string;
/** 配送模板编号 */
deliveryTemplateId?: null | number;
/** 发货物流公司编号 */
logisticsId?: null | number;
/** 发货物流单号 */
logisticsNo?: string;
/** 发货时间 */
deliveryTime?: Date | null;
/** 收货时间 */
receiveTime?: Date | null;
/** 收件人名称 */
receiverName?: string;
/** 收件人手机 */
receiverMobile?: string;
/** 收件人邮编 */
receiverPostCode?: null | number;
/** 收件人地区编号 */
receiverAreaId?: null | number;
/** 收件人地区名字 */
receiverAreaName?: string;
/** 收件人详细地址 */
receiverDetailAddress?: string;
/** 售后状态 */
afterSaleStatus?: null | number;
/** 退款金额 */
refundPrice?: null | number;
/** 优惠劵编号 */
couponId?: null | number;
/** 优惠劵减免金额 */
couponPrice?: null | number;
/** 积分抵扣的金额 */
pointPrice?: null | number;
/** VIP 减免金额 */
vipPrice?: null | number;
/** 订单项列表 */
items?: OrderItem[];
/** 下单用户信息 */
user?: {
/** 用户头像 */
avatar?: string;
/** 用户编号 */
id?: null | number;
/** 用户昵称 */
nickname?: string;
};
/** 推广用户信息 */
brokerageUser?: {
/** 用户头像 */
avatar?: string;
/** 用户编号 */
id?: null | number;
/** 用户昵称 */
nickname?: string;
};
/** 订单操作日志 */
logs?: OrderLog[];
}
/** 交易订单统计 */
export interface OrderSummary {
/** 订单数量 */
orderCount?: number;
/** 订单金额 */
orderPayPrice?: string;
/** 退款单数 */
afterSaleCount?: number;
/** 退款金额 */
afterSalePrice?: string;
}
/** 订单发货请求 */
export interface DeliveryRequest {
/** 订单编号 */
id?: number;
/** 发货方式 */
expressType: string;
/** 物流公司编号 */
logisticsId: null | number;
/** 物流编号 */
logisticsNo: string;
}
/** 订单备注请求 */
export interface RemarkRequest {
/** 订单编号 */
id: number;
/** 备注 */
remark: string;
}
/** 订单调价请求 */
export interface PriceRequest {
/** 订单编号 */
id: number;
/** 调整金额,单位:分 */
adjustPrice: number;
}
/** 订单地址请求 */
export interface AddressRequest {
/** 订单编号 */
id: number;
/** 收件人名称 */
receiverName: string;
/** 收件人手机 */
receiverMobile: string;
/** 收件人地区编号 */
receiverAreaId: number;
/** 收件人详细地址 */
receiverDetailAddress: string;
}
}
/** 查询交易订单列表 */
export function getOrderPage(params: PageParam) {
return requestClient.get<PageResult<MallOrderApi.Order>>(
'/trade/order/page',
{
params,
},
);
}
/** 查询交易订单统计 */
export function getOrderSummary(params: PageParam) {
return requestClient.get<MallOrderApi.OrderSummary>('/trade/order/summary', {
params,
});
}
/** 查询交易订单详情 */
export function getOrder(id: number) {
return requestClient.get<MallOrderApi.Order>(
`/trade/order/get-detail?id=${id}`,
);
}
/** 查询交易订单物流详情 */
export function getExpressTrackList(id: number) {
return requestClient.get(`/trade/order/get-express-track-list?id=${id}`);
}
/** 订单发货 */
export function deliveryOrder(data: MallOrderApi.DeliveryRequest) {
return requestClient.put('/trade/order/delivery', data);
}
/** 订单备注 */
export function updateOrderRemark(data: MallOrderApi.RemarkRequest) {
return requestClient.put('/trade/order/update-remark', data);
}
/** 订单调价 */
export function updateOrderPrice(data: MallOrderApi.PriceRequest) {
return requestClient.put('/trade/order/update-price', data);
}
/** 修改订单地址 */
export function updateOrderAddress(data: MallOrderApi.AddressRequest) {
return requestClient.put('/trade/order/update-address', data);
}
/** 订单核销 */
export function pickUpOrder(id: number) {
return requestClient.put(`/trade/order/pick-up-by-id?id=${id}`);
}
/** 订单核销 */
export function pickUpOrderByVerifyCode(pickUpVerifyCode: string) {
return requestClient.put('/trade/order/pick-up-by-verify-code', {
params: { pickUpVerifyCode },
});
}
/** 查询核销码对应的订单 */
export function getOrderByPickUpVerifyCode(pickUpVerifyCode: string) {
return requestClient.get<MallOrderApi.Order>(
'/trade/order/get-by-pick-up-verify-code',
{ params: { pickUpVerifyCode } },
);
}

View File

@@ -1,3 +1,9 @@
import { defineAsyncComponent } from 'vue';
export const AsyncOperateLog = defineAsyncComponent(
() => import('./operate-log.vue'),
);
export { default as OperateLog } from './operate-log.vue'; export { default as OperateLog } from './operate-log.vue';
export type { OperateLogProps } from './typing'; export type { OperateLogProps } from './typing';

View File

@@ -1,7 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import type { OperateLogProps } from './typing'; import type { OperateLogProps } from './typing';
import { Timeline } from 'ant-design-vue'; import { formatDateTime } from '@vben/utils';
import { Tag, Timeline } from 'ant-design-vue';
import { DICT_TYPE, getDictLabel, getDictObj } from '#/utils'; import { DICT_TYPE, getDictLabel, getDictObj } from '#/utils';
@@ -38,8 +40,21 @@ function getUserTypeColor(userType: number) {
:key="log.id" :key="log.id"
:color="getUserTypeColor(log.userType)" :color="getUserTypeColor(log.userType)"
> >
<p>{{ log.createTime }}</p> <template #dot>
<p>{{ getDictLabel(DICT_TYPE.USER_TYPE, log.userType)[0] }}</p> <p
:style="{ backgroundColor: getUserTypeColor(log.userType) }"
class="absolute left-[-5px] flex h-5 w-5 items-center justify-center rounded-full text-xs text-white"
>
{{ getDictLabel(DICT_TYPE.USER_TYPE, log.userType)[0] }}
</p>
</template>
<p>{{ formatDateTime(log.createTime) }}</p>
<p>
<Tag :color="getUserTypeColor(log.userType)">
{{ log.userName }}
</Tag>
{{ log.action }}
</p>
</Timeline.Item> </Timeline.Item>
</Timeline> </Timeline>
</div> </div>

View File

@@ -9,7 +9,7 @@ import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';
import { Button, Card, Col, Row, Tree } from 'ant-design-vue'; import { Card, Col, Row, Tree } from 'ant-design-vue';
import { getSimpleDeptList } from '#/api/system/dept'; import { getSimpleDeptList } from '#/api/system/dept';
@@ -41,24 +41,6 @@ const emit = defineEmits<{
confirm: [deptList: SystemDeptApi.Dept[]]; confirm: [deptList: SystemDeptApi.Dept[]];
}>(); }>();
// 对话框配置
const [Modal, modalApi] = useVbenModal({
title: props.title,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetData();
return;
}
modalApi.setState({ loading: true });
try {
deptData.value = await getSimpleDeptList();
deptTree.value = handleTree(deptData.value) as DataNode[];
} finally {
modalApi.setState({ loading: false });
}
},
destroyOnClose: true,
});
type checkedKeys = number[] | { checked: number[]; halfChecked: number[] }; type checkedKeys = number[] | { checked: number[]; halfChecked: number[] };
// 部门树形结构 // 部门树形结构
const deptTree = ref<DataNode[]>([]); const deptTree = ref<DataNode[]>([]);
@@ -67,14 +49,40 @@ const selectedDeptIds = ref<checkedKeys>([]);
// 部门数据 // 部门数据
const deptData = ref<SystemDeptApi.Dept[]>([]); const deptData = ref<SystemDeptApi.Dept[]>([]);
/** 打开对话框 */ // 对话框配置
const open = async (selectedList?: SystemDeptApi.Dept[]) => { const [Modal, modalApi] = useVbenModal({
modalApi.open(); async onConfirm() {
// 获取选中的部门ID
const selectedIds: number[] = Array.isArray(selectedDeptIds.value)
? selectedDeptIds.value
: selectedDeptIds.value.checked || [];
const deptArray = deptData.value.filter((dept) =>
selectedIds.includes(dept.id!),
);
emit('confirm', deptArray);
// 关闭并提示
await modalApi.close();
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
deptTree.value = [];
selectedDeptIds.value = [];
return;
}
// 加载数据
const data = modalApi.getData();
if (!data) {
return;
}
modalApi.lock();
try {
deptData.value = await getSimpleDeptList();
deptTree.value = handleTree(deptData.value) as DataNode[];
// // 设置已选择的部门 // // 设置已选择的部门
if (selectedList?.length) { if (data.selectedList?.length) {
const selectedIds = selectedList const selectedIds = data.selectedList
.map((dept) => dept.id) .map((dept: SystemDeptApi.Dept) => dept.id)
.filter((id): id is number => id !== undefined); .filter((id: number) => id !== undefined);
selectedDeptIds.value = props.checkStrictly selectedDeptIds.value = props.checkStrictly
? { ? {
checked: selectedIds, checked: selectedIds,
@@ -82,10 +90,15 @@ const open = async (selectedList?: SystemDeptApi.Dept[]) => {
} }
: selectedIds; : selectedIds;
} }
}; } finally {
modalApi.unlock();
}
},
destroyOnClose: true,
});
/** 处理选中状态变化 */ /** 处理选中状态变化 */
const handleCheck = () => { function handleCheck() {
if (!props.multiple) { if (!props.multiple) {
// 单选模式下,只保留最后选择的节点 // 单选模式下,只保留最后选择的节点
if (Array.isArray(selectedDeptIds.value)) { if (Array.isArray(selectedDeptIds.value)) {
@@ -106,37 +119,10 @@ const handleCheck = () => {
} }
} }
} }
}; }
/** 提交选择 */
const handleConfirm = async () => {
// 获取选中的部门ID
const selectedIds: number[] = Array.isArray(selectedDeptIds.value)
? selectedDeptIds.value
: selectedDeptIds.value.checked || [];
const deptArray = deptData.value.filter((dept) =>
selectedIds.includes(dept.id!),
);
// 关闭并提示
await modalApi.close();
emit('confirm', deptArray);
};
const handleCancel = () => {
modalApi.close();
};
/** 重置数据 */
const resetData = () => {
deptTree.value = [];
selectedDeptIds.value = [];
};
/** 提供 open 方法,用于打开对话框 */
defineExpose({ open });
</script> </script>
<template> <template>
<Modal> <Modal :title="title" key="dept-select-modal" class="w-[40%]">
<Row class="h-full"> <Row class="h-full">
<Col :span="24"> <Col :span="24">
<Card class="h-full"> <Card class="h-full">
@@ -153,9 +139,5 @@ defineExpose({ open });
</Card> </Card>
</Col> </Col>
</Row> </Row>
<template #footer>
<Button @click="handleCancel">{{ cancelText }}</Button>
<Button type="primary" @click="handleConfirm">{{ confirmText }}</Button>
</template>
</Modal> </Modal>
</template> </template>

View File

@@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
// TODO @芋艿:是否有更好的组织形式?! // TODO @芋艿:是否有更好的组织形式?!
// TODO @xingyu你感觉这个放到每个 system、infra 模块下,然后新建一个 components表示每个模块有一些共享的组件然后全局只放通用的无业务含义的可以哇
import type { Key } from 'ant-design-vue/es/table/interface'; import type { Key } from 'ant-design-vue/es/table/interface';
import type { SystemDeptApi } from '#/api/system/dept'; import type { SystemDeptApi } from '#/api/system/dept';
@@ -17,7 +18,6 @@ import {
message, message,
Pagination, Pagination,
Row, Row,
Spin,
Transfer, Transfer,
Tree, Tree,
} from 'ant-design-vue'; } from 'ant-design-vue';
@@ -66,16 +66,66 @@ const expandedKeys = ref<Key[]>([]);
const selectedDeptId = ref<number>(); const selectedDeptId = ref<number>();
const deptSearchKeys = ref(''); const deptSearchKeys = ref('');
// 加载状态
const loading = ref(false);
// 用户数据管理 // 用户数据管理
const userList = ref<SystemUserApi.User[]>([]); // 存储所有已知用户 const userList = ref<SystemUserApi.User[]>([]); // 存储所有已知用户
const selectedUserIds = ref<string[]>([]); const selectedUserIds = ref<string[]>([]);
// 弹窗配置
const [Modal, modalApi] = useVbenModal({
onCancel: handleCancel,
onClosed: handleClosed,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetData();
return;
}
// 加载数据
const data = modalApi.getData();
if (!data) {
return;
}
modalApi.lock();
try {
// 加载部门数据
const deptData = await getSimpleDeptList();
deptList.value = deptData;
const treeData = handleTree(deptData);
deptTree.value = treeData.map((node) => processDeptNode(node));
expandedKeys.value = deptTree.value.map((node) => node.key);
// 加载初始用户数据
await loadUserData(1, leftListState.value.pagination.pageSize);
// 设置已选用户
if (data.userIds?.length) {
selectedUserIds.value = data.userIds.map(String);
// 加载已选用户的完整信息 TODO 目前接口暂不支持 多个用户ID 查询, 需要后端支持
const { list } = await getUserPage({
pageNo: 1,
pageSize: 100, // 临时使用固定值确保能加载所有已选用户
userIds: data.userIds,
});
// 使用 Map 来去重,以用户 ID 为 key
const userMap = new Map(userList.value.map((user) => [user.id, user]));
list.forEach((user) => {
if (!userMap.has(user.id)) {
userMap.set(user.id, user);
}
});
userList.value = [...userMap.values()];
updateRightListData();
}
modalApi.open();
} finally {
modalApi.unlock();
}
},
destroyOnClose: true,
});
// 左侧列表状态 // 左侧列表状态
const leftListState = ref({ const leftListState = ref({
loading: false,
searchValue: '', searchValue: '',
dataSource: [] as SystemUserApi.User[], dataSource: [] as SystemUserApi.User[],
pagination: { pagination: {
@@ -145,8 +195,7 @@ const filteredDeptTree = computed(() => {
}); });
// 加载用户数据 // 加载用户数据
const loadUserData = async (pageNo: number, pageSize: number) => { async function loadUserData(pageNo: number, pageSize: number) {
leftListState.value.loading = true;
try { try {
const { list, total } = await getUserPage({ const { list, total } = await getUserPage({
pageNo, pageNo,
@@ -167,13 +216,11 @@ const loadUserData = async (pageNo: number, pageSize: number) => {
if (newUsers.length > 0) { if (newUsers.length > 0) {
userList.value.push(...newUsers); userList.value.push(...newUsers);
} }
} finally { } finally {}
leftListState.value.loading = false;
} }
};
// 更新右侧列表数据 // 更新右侧列表数据
const updateRightListData = () => { function updateRightListData() {
// 使用 Set 来去重选中的用户ID // 使用 Set 来去重选中的用户ID
const uniqueSelectedIds = new Set(selectedUserIds.value); const uniqueSelectedIds = new Set(selectedUserIds.value);
@@ -202,22 +249,22 @@ const updateRightListData = () => {
const endIndex = startIndex + pageSize; const endIndex = startIndex + pageSize;
rightListState.value.dataSource = filteredUsers.slice(startIndex, endIndex); rightListState.value.dataSource = filteredUsers.slice(startIndex, endIndex);
}; }
// 处理左侧分页变化 // 处理左侧分页变化
const handleLeftPaginationChange = async (page: number, pageSize: number) => { async function handleLeftPaginationChange(page: number, pageSize: number) {
await loadUserData(page, pageSize); await loadUserData(page, pageSize);
}; }
// 处理右侧分页变化 // 处理右侧分页变化
const handleRightPaginationChange = (page: number, pageSize: number) => { function handleRightPaginationChange(page: number, pageSize: number) {
rightListState.value.pagination.current = page; rightListState.value.pagination.current = page;
rightListState.value.pagination.pageSize = pageSize; rightListState.value.pagination.pageSize = pageSize;
updateRightListData(); updateRightListData();
}; }
// 处理用户搜索 // 处理用户搜索
const handleUserSearch = async (direction: string, value: string) => { async function handleUserSearch(direction: string, value: string) {
if (direction === 'left') { if (direction === 'left') {
leftListState.value.searchValue = value; leftListState.value.searchValue = value;
leftListState.value.pagination.current = 1; leftListState.value.pagination.current = 1;
@@ -227,18 +274,18 @@ const handleUserSearch = async (direction: string, value: string) => {
rightListState.value.pagination.current = 1; rightListState.value.pagination.current = 1;
updateRightListData(); updateRightListData();
} }
}; }
// 处理用户选择变化 // 处理用户选择变化
const handleUserChange = (targetKeys: string[]) => { function handleUserChange(targetKeys: string[]) {
// 使用 Set 来去重选中的用户ID // 使用 Set 来去重选中的用户ID
selectedUserIds.value = [...new Set(targetKeys)]; selectedUserIds.value = [...new Set(targetKeys)];
emit('update:value', selectedUserIds.value.map(Number)); emit('update:value', selectedUserIds.value.map(Number));
updateRightListData(); updateRightListData();
}; }
// 重置数据 // 重置数据
const resetData = () => { function resetData() {
userList.value = []; userList.value = [];
selectedUserIds.value = []; selectedUserIds.value = [];
@@ -249,7 +296,6 @@ const resetData = () => {
selectedUserIds.value = []; selectedUserIds.value = [];
leftListState.value = { leftListState.value = {
loading: false,
searchValue: '', searchValue: '',
dataSource: [], dataSource: [],
pagination: { pagination: {
@@ -268,61 +314,20 @@ const resetData = () => {
total: 0, total: 0,
}, },
}; };
};
// 打开弹窗
const open = async (userIds: string[]) => {
resetData();
loading.value = true;
try {
// 加载部门数据
const deptData = await getSimpleDeptList();
deptList.value = deptData;
const treeData = handleTree(deptData);
deptTree.value = treeData.map((node) => processDeptNode(node));
expandedKeys.value = deptTree.value.map((node) => node.key);
// 加载初始用户数据
await loadUserData(1, leftListState.value.pagination.pageSize);
// 设置已选用户
if (userIds?.length) {
selectedUserIds.value = userIds.map(String);
// 加载已选用户的完整信息 TODO 目前接口暂不支持 多个用户ID 查询, 需要后端支持
const { list } = await getUserPage({
pageNo: 1,
pageSize: 100, // 临时使用固定值确保能加载所有已选用户
userIds,
});
// 使用 Map 来去重,以用户 ID 为 key
const userMap = new Map(userList.value.map((user) => [user.id, user]));
list.forEach((user) => {
if (!userMap.has(user.id)) {
userMap.set(user.id, user);
} }
});
userList.value = [...userMap.values()];
updateRightListData();
}
modalApi.open();
} finally {
loading.value = false;
}
};
// TODO 后端接口目前仅支持 username 检索, 筛选条件需要跟后端请求参数保持一致。 // TODO 后端接口目前仅支持 username 检索, 筛选条件需要跟后端请求参数保持一致。
const filterOption = (inputValue: string, option: any) => { function filterOption(inputValue: string, option: any) {
return option.username.toLowerCase().includes(inputValue.toLowerCase()); return option.username.toLowerCase().includes(inputValue.toLowerCase());
}; }
// 处理部门树展开/折叠 // 处理部门树展开/折叠
const handleExpand = (keys: Key[]) => { function handleExpand(keys: Key[]) {
expandedKeys.value = keys; expandedKeys.value = keys;
}; }
// 处理部门搜索 // 处理部门搜索
const handleDeptSearch = (value: string) => { function handleDeptSearch(value: string) {
deptSearchKeys.value = value; deptSearchKeys.value = value;
// 如果有搜索结果,自动展开所有节点 // 如果有搜索结果,自动展开所有节点
@@ -342,10 +347,10 @@ const handleDeptSearch = (value: string) => {
// 清空搜索时,只展开第一级节点 // 清空搜索时,只展开第一级节点
expandedKeys.value = deptTree.value.map((node) => node.key); expandedKeys.value = deptTree.value.map((node) => node.key);
} }
}; }
// 处理部门选择 // 处理部门选择
const handleDeptSelect = async (selectedKeys: Key[], _info: any) => { async function handleDeptSelect(selectedKeys: Key[], _info: any) {
// 更新选中的部门ID // 更新选中的部门ID
const newDeptId = const newDeptId =
selectedKeys.length > 0 ? Number(selectedKeys[0]) : undefined; selectedKeys.length > 0 ? Number(selectedKeys[0]) : undefined;
@@ -356,10 +361,10 @@ const handleDeptSelect = async (selectedKeys: Key[], _info: any) => {
const { pageSize } = leftListState.value.pagination; const { pageSize } = leftListState.value.pagination;
leftListState.value.pagination.current = 1; leftListState.value.pagination.current = 1;
await loadUserData(1, pageSize); await loadUserData(1, pageSize);
}; }
// 确认选择 // 确认选择
const handleConfirm = () => { function handleConfirm() {
if (selectedUserIds.value.length === 0) { if (selectedUserIds.value.length === 0) {
message.warning('请选择用户'); message.warning('请选择用户');
return; return;
@@ -371,50 +376,37 @@ const handleConfirm = () => {
), ),
); );
modalApi.close(); modalApi.close();
}; }
// 取消选择 // 取消选择
const handleCancel = () => { function handleCancel() {
emit('cancel'); emit('cancel');
modalApi.close(); modalApi.close();
// 确保在动画结束后再重置数据 // 确保在动画结束后再重置数据
setTimeout(() => { setTimeout(() => {
resetData(); resetData();
}, 300); }, 300);
}; }
// 关闭弹窗 // 关闭弹窗
const handleClosed = () => { function handleClosed() {
emit('closed'); emit('closed');
resetData(); resetData();
}; }
// 弹窗配置
const [ModalComponent, modalApi] = useVbenModal({
title: props.title,
onCancel: handleCancel,
onClosed: handleClosed,
destroyOnClose: true,
});
// 递归处理部门树节点 // 递归处理部门树节点
const processDeptNode = (node: any): DeptTreeNode => { function processDeptNode(node: any): DeptTreeNode {
return { return {
key: String(node.id), key: String(node.id),
title: `${node.name} (${node.id})`, title: `${node.name} (${node.id})`,
name: node.name, name: node.name,
children: node.children?.map((child: any) => processDeptNode(child)), children: node.children?.map((child: any) => processDeptNode(child)),
}; };
}; }
defineExpose({
open,
});
</script> </script>
<template> <template>
<ModalComponent class="w-[1000px]" key="user-select-modal"> <Modal class="w-[40%]" key="user-select-modal" :title="title">
<Spin :spinning="loading">
<Row :gutter="[16, 16]"> <Row :gutter="[16, 16]">
<Col :span="6"> <Col :span="6">
<div class="h-[500px] overflow-auto rounded border"> <div class="h-[500px] overflow-auto rounded border">
@@ -479,7 +471,6 @@ defineExpose({
</Transfer> </Transfer>
</Col> </Col>
</Row> </Row>
</Spin>
<template #footer> <template #footer>
<Button <Button
type="primary" type="primary"
@@ -490,7 +481,7 @@ defineExpose({
</Button> </Button>
<Button @click="handleCancel">{{ cancelText }}</Button> <Button @click="handleCancel">{{ cancelText }}</Button>
</template> </template>
</ModalComponent> </Modal>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -201,7 +201,7 @@ function getRouterNode(node: any) {
defineExpose({ openDrawer }); // 暴露方法给父组件 defineExpose({ openDrawer }); // 暴露方法给父组件
</script> </script>
<template> <template>
<Drawer class="w-[630px]"> <Drawer class="w-[40%]">
<template #title> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Input <Input

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO @芋艿:后续是不是把业务组件,挪到每个模块里;待定;
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';

View File

@@ -235,7 +235,7 @@ defineExpose({ validate });
:readonly="false" :readonly="false"
@save="saveSimpleFlowModel" @save="saveSimpleFlowModel"
/> />
<ErrorModal title="流程设计校验不通过" class="w-[600px]"> <ErrorModal title="流程设计校验不通过" class="w-[40%]">
<div class="mb-2 text-base">以下节点配置不完善请修改相关配置</div> <div class="mb-2 text-base">以下节点配置不完善请修改相关配置</div>
<div <div
class="mb-3 rounded-md bg-gray-100 p-2 text-sm" class="mb-3 rounded-md bg-gray-100 p-2 text-sm"

View File

@@ -100,7 +100,6 @@ async function handleRemove(file: UploadFile) {
} }
async function beforeUpload(file: File) { async function beforeUpload(file: File) {
// 使用现代的Blob.text()方法替代FileReader
const fileContent = await file.text(); const fileContent = await file.text();
emit('returnText', fileContent); emit('returnText', fileContent);

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO @xingyu这个组件只有 pay 在用,和现有的 file-upload 和 image-upload 有点不一致。是不是可以考虑移除,只在 pay 那搞个复用的组件;
import type { InputProps, TextAreaProps } from 'ant-design-vue'; import type { InputProps, TextAreaProps } from 'ant-design-vue';
import type { FileUploadProps } from './typing'; import type { FileUploadProps } from './typing';

View File

@@ -0,0 +1,76 @@
// import type { RouteRecordRaw } from 'vue-router';
// const routes: RouteRecordRaw[] = [
// {
// path: '/mall/product',
// name: 'ProductCenter',
// meta: {
// title: '商品中心',
// icon: 'lucide:shopping-bag',
// keepAlive: true,
// hideInMenu: true,
// },
// children: [
// {
// path: 'spu/add',
// name: 'ProductSpuAdd',
// meta: {
// title: '商品添加',
// activeMenu: '/mall/product/spu',
// },
// component: () => import('#/views/mall/product/spu/form/index.vue'),
// },
// {
// path: String.raw`spu/edit/:id(\d+)`,
// name: 'ProductSpuEdit',
// meta: {
// title: '商品编辑',
// activeMenu: '/mall/product/spu',
// },
// component: () => import('#/views/mall/product/spu/form/index.vue'),
// },
// {
// path: String.raw`spu/detail/:id(\d+)`,
// name: 'ProductSpuDetail',
// meta: {
// title: '商品详情',
// activeMenu: '/crm/business',
// },
// component: () => import('#/views/mall/product/spu/form/index.vue'),
// },
// ],
// },
// {
// path: '/mall/trade',
// name: 'TradeCenter',
// meta: {
// title: '交易中心',
// icon: 'lucide:shopping-cart',
// keepAlive: true,
// hideInMenu: true,
// },
// children: [
// {
// path: String.raw`order/detail/:id(\d+)`,
// name: 'TradeOrderDetail',
// meta: {
// title: '订单详情',
// activeMenu: '/mall/trade/order',
// },
// component: () => import('#/views/mall/trade/order/detail/index.vue'),
// },
// {
// path: String.raw`after-sale/detail/:id(\d+)`,
// name: 'TradeAfterSaleDetail',
// meta: {
// title: '退款详情',
// activeMenu: '/mall/trade/after-sale',
// },
// component: () =>
// import('#/views/mall/trade/afterSale/detail/index.vue'),
// },
// ],
// },
// ];
// export default routes;

View File

@@ -2,7 +2,7 @@
* 下载工具模块 * 下载工具模块
* 提供多种文件格式的下载功能 * 提供多种文件格式的下载功能
*/ */
// 请使用 @vben/utils/download 代替 packages/@core/base/shared/src/utils/download.ts // TODO @ziye请使用 @vben/utils/download 代替 packages/@core/base/shared/src/utils/download.ts
/** /**
* 图片下载配置接口 * 图片下载配置接口

View File

@@ -1,3 +1,4 @@
// TODO @xingyu感觉 formatToFraction 可以整合起来;【优先级:低】
/** /**
* 将一个整数转换为分数保留两位小数 * 将一个整数转换为分数保留两位小数
* @param num * @param num

View File

@@ -1,32 +0,0 @@
/**
* 将毫秒转换成时间字符串。例如说xx 分钟
*
* @param ms 毫秒
* @returns {string} 字符串
*/
// TODO @xingyu这个要融合到哪里去 date 么?
export function formatPast2(ms: number): string {
// 定义时间单位常量,便于维护
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
// 计算各时间单位
const day = Math.floor(ms / DAY);
const hour = Math.floor((ms % DAY) / HOUR);
const minute = Math.floor((ms % HOUR) / MINUTE);
const second = Math.floor((ms % MINUTE) / SECOND);
// 根据时间长短返回不同格式
if (day > 0) {
return `${day}${hour} 小时 ${minute} 分钟`;
}
if (hour > 0) {
return `${hour} 小时 ${minute} 分钟`;
}
if (minute > 0) {
return `${minute} 分钟`;
}
return second > 0 ? `${second}` : `${0}`;
}

View File

@@ -2,7 +2,6 @@ export * from './constants';
export * from './dict'; export * from './dict';
export * from './download'; export * from './download';
export * from './formatNumber'; export * from './formatNumber';
export * from './formatTime';
export * from './formCreate'; export * from './formCreate';
export * from './rangePickerProps'; export * from './rangePickerProps';
export * from './routerHelper'; export * from './routerHelper';

View File

@@ -1,7 +1,7 @@
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const modules = import.meta.glob('../views/**/*.{vue,tsx}'); const modules = import.meta.glob('../views/**/*.{vue,tsx}');
// TODO @xingyu这个要不要融合到哪个 router util 里? // TODO @xingyu这个要不要融合到哪个 router util 里? utils 里面没有引入 vue 使用不了 defineAsyncComponent
/** /**
* 注册一个异步组件 * 注册一个异步组件
* @param componentPath 例:/bpm/oa/leave/detail * @param componentPath 例:/bpm/oa/leave/detail

View File

@@ -1,13 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmCategoryApi } from '#/api/bpm/category';
import { useAccess } from '@vben/access';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -106,9 +102,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmCategoryApi.CategoryVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'id', field: 'id',
@@ -146,29 +140,10 @@ export function useGridColumns<T = BpmCategoryApi.CategoryVO>(
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 180, width: 180,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '流程分类',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['bpm:category:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['bpm:category:delete']),
},
],
},
}, },
]; ];
} }

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { import type { VxeTableGridOptions } from '#/adapter/vxe-table';
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { BpmCategoryApi } from '#/api/bpm/category'; import type { BpmCategoryApi } from '#/api/bpm/category';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteCategory, getCategoryPage } from '#/api/bpm/category'; import { deleteCategory, getCategoryPage } from '#/api/bpm/category';
import { DocAlert } from '#/components/doc-alert'; import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales'; import { $t } from '#/locales';
@@ -22,12 +18,46 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form, connectedComponent: Form,
destroyOnClose: true, destroyOnClose: true,
}); });
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建流程分类 */
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑流程分类 */
function handleEdit(row: BpmCategoryApi.CategoryVO) {
formModalApi.setData(row).open();
}
/** 删除流程分类 */
async function handleDelete(row: BpmCategoryApi.CategoryVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.code]),
key: 'action_key_msg',
});
try {
await deleteCategory(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.code]),
key: 'action_key_msg',
});
onRefresh();
} catch {
hideLoading();
}
}
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(onActionClick), columns: useGridColumns(),
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
proxyConfig: { proxyConfig: {
@@ -50,54 +80,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<BpmCategoryApi.CategoryVO>, } as VxeTableGridOptions<BpmCategoryApi.CategoryVO>,
}); });
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<BpmCategoryApi.CategoryVO>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建流程分类 */
function onCreate() {
formModalApi.setData(null).open();
}
/** 编辑流程分类 */
function onEdit(row: BpmCategoryApi.CategoryVO) {
formModalApi.setData(row).open();
}
/** 删除流程分类 */
async function onDelete(row: BpmCategoryApi.CategoryVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.code]),
duration: 0,
key: 'action_process_msg',
});
try {
await deleteCategory(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.code]));
onRefresh();
} catch {
hideLoading();
}
}
</script> </script>
<template> <template>
@@ -109,14 +91,41 @@ async function onDelete(row: BpmCategoryApi.CategoryVO) {
<FormModal @success="onRefresh" /> <FormModal @success="onRefresh" />
<Grid table-title="流程分类"> <Grid table-title="流程分类">
<template #toolbar-tools> <template #toolbar-tools>
<Button <TableAction
type="primary" :actions="[
@click="onCreate" {
v-access:code="['bpm:category:create']" label: $t('ui.actionTitle.create', ['流程分类']),
> type: 'primary',
<Plus class="size-5" /> icon: ACTION_ICON.ADD,
{{ $t('ui.actionTitle.create', ['流程分类']) }} auth: ['bpm:category:create'],
</Button> onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['bpm:category:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['bpm:category:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template> </template>
</Grid> </Grid>
</Page> </Page>

View File

@@ -1,15 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmFormApi } from '#/api/bpm/form';
import { useAccess } from '@vben/access';
import { $t } from '@vben/locales';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -68,9 +62,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmFormApi.FormVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'id', field: 'id',
@@ -103,41 +95,10 @@ export function useGridColumns<T = BpmFormApi.FormVO>(
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 200, width: 240,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '流程名称',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'copy',
text: $t('ui.actionTitle.copy'),
show: hasAccessByCodes(['bpm:form:update']),
},
{
code: 'edit',
text: $t('ui.actionTitle.edit'),
show: hasAccessByCodes(['bpm:form:update']),
},
{
code: 'detail',
text: $t('ui.actionTitle.detail'),
show: hasAccessByCodes(['bpm:form:query']),
},
{
code: 'delete',
text: $t('ui.actionTitle.delete'),
show: hasAccessByCodes(['bpm:form:delete']),
},
],
},
}, },
]; ];
} }

View File

@@ -106,6 +106,7 @@ async function initializeDesigner() {
} }
} }
// TODO @ziye注释使用 /** */ 风格,高亮更明显哈,方法注释;
// 保存表单 // 保存表单
function handleSave() { function handleSave() {
formModalApi formModalApi

View File

@@ -1,36 +1,98 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { import type { VxeTableGridOptions } from '#/adapter/vxe-table';
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { BpmFormApi } from '#/api/bpm/form'; import type { BpmFormApi } from '#/api/bpm/form';
import { ref, watch } from 'vue'; import { watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import FormCreate from '@form-create/ant-design-vue'; import { message } from 'ant-design-vue';
import { Button, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteForm, getFormDetail, getFormPage } from '#/api/bpm/form'; import { deleteForm, getFormPage } from '#/api/bpm/form';
import { DocAlert } from '#/components/doc-alert'; import { DocAlert } from '#/components/doc-alert';
import { router } from '#/router'; import { router } from '#/router';
import { setConfAndFields2 } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
import Detail from './modules/detail.vue';
defineOptions({ name: 'BpmForm' }); defineOptions({ name: 'BpmForm' });
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 新增 */
function handleCreate() {
router.push({
name: 'BpmFormEditor',
query: {
type: 'create',
},
});
}
/** 编辑 */
function handleEdit(row: BpmFormApi.FormVO) {
router.push({
name: 'BpmFormEditor',
query: {
id: row.id,
type: 'edit',
},
});
}
/** 复制 */
function handleCopy(row: BpmFormApi.FormVO) {
router.push({
name: 'BpmFormEditor',
query: {
copyId: row.id,
type: 'copy',
},
});
}
/** 删除 */
async function handleDelete(row: BpmFormApi.FormVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
key: 'action_key_msg',
});
try {
await deleteForm(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} finally {
hideLoading();
}
}
async function handleDetail(row: BpmFormApi.FormVO) {
detailModalApi.setData(row).open();
}
/** 详情弹窗 */
const [DetailModal, detailModalApi] = useVbenModal({
connectedComponent: Detail,
destroyOnClose: true,
});
/** 检测路由参数 */
const route = useRoute();
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(onActionClick), columns: useGridColumns(),
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
proxyConfig: { proxyConfig: {
@@ -57,101 +119,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
} as VxeTableGridOptions<BpmFormApi.FormVO>, } as VxeTableGridOptions<BpmFormApi.FormVO>,
}); });
/** 表格操作按钮的回调函数 */
function onActionClick({ code, row }: OnActionClickParams<BpmFormApi.FormVO>) {
switch (code) {
case 'copy': {
onCopy(row);
break;
}
case 'delete': {
onDelete(row);
break;
}
case 'detail': {
onDetail(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 复制 */
function onCopy(row: BpmFormApi.FormVO) {
router.push({
name: 'BpmFormEditor',
query: {
copyId: row.id,
type: 'copy',
},
});
}
/** 删除 */
async function onDelete(row: BpmFormApi.FormVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.id]),
duration: 0,
key: 'action_process_msg',
});
try {
await deleteForm(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
onRefresh();
} finally {
hideLoading();
}
}
/** 详情 */
const formConfig = ref<any>({});
async function onDetail(row: BpmFormApi.FormVO) {
formConfig.value = await getFormDetail(row.id as number);
setConfAndFields2(
formConfig.value,
formConfig.value.conf,
formConfig.value.fields,
);
detailModalApi.open();
}
/** 编辑 */
function onEdit(row: BpmFormApi.FormVO) {
router.push({
name: 'BpmFormEditor',
query: {
id: row.id,
type: 'edit',
},
});
}
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 新增 */
function onCreate() {
router.push({
name: 'BpmFormEditor',
query: {
type: 'create',
},
});
}
/** 详情弹窗 */
const [DetailModal, detailModalApi] = useVbenModal({
destroyOnClose: true,
footer: false,
});
/** 检测路由参数 */
const route = useRoute();
watch( watch(
() => route.query.refresh, () => route.query.refresh,
(val) => { (val) => {
@@ -171,25 +138,60 @@ watch(
url="https://doc.iocoder.cn/bpm/use-bpm-form/" url="https://doc.iocoder.cn/bpm/use-bpm-form/"
/> />
</template> </template>
<DetailModal />
<Grid table-title="流程表单"> <Grid table-title="流程表单">
<template #toolbar-tools> <template #toolbar-tools>
<Button type="primary" @click="onCreate"> <TableAction
<Plus class="size-5" /> :actions="[
{{ $t('ui.actionTitle.create', ['流程表单']) }} {
</Button> label: $t('ui.actionTitle.create', ['流程表单']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['bpm:form:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('ui.actionTitle.copy'),
type: 'link',
icon: ACTION_ICON.COPY,
auth: ['bpm:form:update'],
onClick: handleCopy.bind(null, row),
},
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['bpm:form:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['bpm:form:query'],
onClick: handleDetail.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['bpm:form:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template> </template>
</Grid> </Grid>
<DetailModal
title="流程表单详情"
class="w-[800px]"
:body-style="{
maxHeight: '100px',
}"
>
<div class="mx-4">
<FormCreate :option="formConfig.option" :rule="formConfig.rule" />
</div>
</DetailModal>
</Page> </Page>
</template> </template>

View File

@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import FormCreate from '@form-create/ant-design-vue';
import { getFormDetail } from '#/api/bpm/form';
import { setConfAndFields2 } from '#/utils';
/** 详情 */
const formConfig = ref<any>({});
const [Modal, modalApi] = useVbenModal({
footer: false,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
return;
}
// 加载数据
const data = modalApi.getData();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
formConfig.value = await getFormDetail(data.id as number);
setConfAndFields2(
formConfig.value,
formConfig.value.conf,
formConfig.value.fields,
);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal
class="w-[40%]"
title="流程表单详情"
:body-style="{
maxHeight: '100px',
}"
>
<FormCreate :option="formConfig.option" :rule="formConfig.rule" />
</Modal>
</template>

View File

@@ -106,7 +106,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle" class="w-[600px]"> <Modal :title="getTitle" class="w-[40%]">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@@ -1,14 +1,10 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmCategoryApi } from '#/api/bpm/category';
import { useAccess } from '@vben/access';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -99,10 +95,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmCategoryApi.CategoryVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
getMemberNames: (userIds: number[]) => string,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'id', field: 'id',
@@ -123,9 +116,7 @@ export function useGridColumns<T = BpmCategoryApi.CategoryVO>(
field: 'userIds', field: 'userIds',
title: '成员', title: '成员',
minWidth: 200, minWidth: 200,
formatter: ({ cellValue }) => { slots: { default: 'userIds' },
return getMemberNames(cellValue);
},
}, },
{ {
field: 'status', field: 'status',
@@ -143,29 +134,10 @@ export function useGridColumns<T = BpmCategoryApi.CategoryVO>(
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 180, width: 180,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '用户分组',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['bpm:user-group:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['bpm:user-group:delete']),
},
],
},
}, },
]; ];
} }

View File

@@ -1,19 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { import type { VxeTableGridOptions } from '#/adapter/vxe-table';
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { BpmUserGroupApi } from '#/api/bpm/userGroup'; import type { BpmUserGroupApi } from '#/api/bpm/userGroup';
import type { SystemUserApi } from '#/api/system/user'; import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue'; import { message, Tag } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteUserGroup, getUserGroupPage } from '#/api/bpm/userGroup'; import { deleteUserGroup, getUserGroupPage } from '#/api/bpm/userGroup';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { DocAlert } from '#/components/doc-alert'; import { DocAlert } from '#/components/doc-alert';
@@ -26,12 +22,53 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form, connectedComponent: Form,
destroyOnClose: true, destroyOnClose: true,
}); });
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建用户分组 */
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑用户分组 */
function handleEdit(row: BpmUserGroupApi.UserGroupVO) {
formModalApi.setData(row).open();
}
/** 删除用户分组 */
async function handleDelete(row: BpmUserGroupApi.UserGroupVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
key: 'action_key_msg',
});
try {
await deleteUserGroup(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
hideLoading();
}
}
const userList = ref<SystemUserApi.User[]>([]);
/** 初始化 */
onMounted(async () => {
// 加载用户列表
userList.value = await getSimpleUserList();
});
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(onActionClick, getMemberNames), columns: useGridColumns(),
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
proxyConfig: { proxyConfig: {
@@ -54,73 +91,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<BpmUserGroupApi.UserGroupVO>, } as VxeTableGridOptions<BpmUserGroupApi.UserGroupVO>,
}); });
/** 获取分组成员姓名 */
function getMemberNames(userIds: number[]) {
const userMap = new Map(
userList.value.map((user) => [user.id, user.nickname]),
);
return userIds
.map((userId) => userMap.get(userId))
.filter(Boolean)
.join('、');
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<BpmUserGroupApi.UserGroupVO>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建用户分组 */
function onCreate() {
formModalApi.setData(null).open();
}
/** 编辑用户分组 */
function onEdit(row: BpmUserGroupApi.UserGroupVO) {
formModalApi.setData(row).open();
}
/** 删除用户分组 */
async function onDelete(row: BpmUserGroupApi.UserGroupVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
});
try {
await deleteUserGroup(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
onRefresh();
} catch {
hideLoading();
}
}
//
const userList = ref<SystemUserApi.User[]>([]);
/** 初始化 */
onMounted(async () => {
// 加载用户列表
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -132,18 +102,46 @@ onMounted(async () => {
<FormModal @success="onRefresh" /> <FormModal @success="onRefresh" />
<Grid table-title="用户分组"> <Grid table-title="用户分组">
<template #toolbar-tools> <template #toolbar-tools>
<Button <TableAction
type="primary" :actions="[
@click="onCreate" {
v-access:code="['bpm:category:create']" label: $t('ui.actionTitle.create', ['用户分组']),
> type: 'primary',
<Plus class="size-5" /> icon: ACTION_ICON.ADD,
{{ $t('ui.actionTitle.create', ['用户分组']) }} auth: ['bpm:user-group:create'],
</Button> onClick: handleCreate,
},
]"
/>
</template> </template>
<template #userIds="{ row }">
<template #userIds-cell="{ row }"> <Tag v-for="userId in row.userIds" :key="userId" color="blue">
<span>{{ row.nicknames }}</span> {{ userList.find((u) => u.id === userId)?.nickname }}
</Tag>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['bpm:user-group:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['bpm:user-group:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template> </template>
</Grid> </Grid>
</Page> </Page>

View File

@@ -64,7 +64,6 @@ const [Modal, modalApi] = useVbenModal({
}, },
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
formData.value = undefined;
return; return;
} }
// 加载数据 // 加载数据
@@ -85,7 +84,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-[40%]">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@@ -60,24 +60,24 @@ const processDesignRef = ref<InstanceType<typeof ProcessDesign>>();
const extraSettingRef = ref<InstanceType<typeof ExtraSetting>>(); const extraSettingRef = ref<InstanceType<typeof ExtraSetting>>();
/** 步骤校验函数 */ /** 步骤校验函数 */
const validateBasic = async () => { async function validateBasic() {
await basicInfoRef.value?.validate(); await basicInfoRef.value?.validate();
}; }
/** 表单设计校验 */ /** 表单设计校验 */
const validateForm = async () => { async function validateForm() {
await formDesignRef.value?.validate(); await formDesignRef.value?.validate();
}; }
/** 流程设计校验 */ /** 流程设计校验 */
const validateProcess = async () => { async function validateProcess() {
await processDesignRef.value?.validate(); await processDesignRef.value?.validate();
}; }
/** 更多设置校验 */ /** 更多设置校验 */
const validateExtra = async () => { async function validateExtra() {
await extraSettingRef.value?.validate(); await extraSettingRef.value?.validate();
}; }
const currentStep = ref(-1); // 步骤控制。-1 用于,一开始全部不展示等当前页面数据初始化完成 const currentStep = ref(-1); // 步骤控制。-1 用于,一开始全部不展示等当前页面数据初始化完成
@@ -139,7 +139,7 @@ const deptList = ref<SystemDeptApi.Dept[]>([]);
/** 初始化数据 */ /** 初始化数据 */
const actionType = route.params.type as string; const actionType = route.params.type as string;
const initData = async () => { async function initData() {
if (actionType === 'definition') { if (actionType === 'definition') {
// 情况一:流程定义场景(恢复) // 情况一:流程定义场景(恢复)
const definitionId = route.params.id as string; const definitionId = route.params.id as string;
@@ -200,7 +200,7 @@ const initData = async () => {
// 以前未配置更多设置的流程 // 以前未配置更多设置的流程
extraSettingRef.value?.initData(); extraSettingRef.value?.initData();
}; }
/** 根据类型切换流程数据 */ /** 根据类型切换流程数据 */
watch( watch(
@@ -218,7 +218,7 @@ watch(
); );
/** 校验所有步骤数据是否完整 */ /** 校验所有步骤数据是否完整 */
const validateAllSteps = async () => { async function validateAllSteps() {
// 基本信息校验 // 基本信息校验
try { try {
await validateBasic(); await validateBasic();
@@ -254,10 +254,10 @@ const validateAllSteps = async () => {
} }
return true; return true;
}; }
/** 保存操作 */ /** 保存操作 */
const handleSave = async () => { async function handleSave() {
try { try {
// 保存前校验所有步骤的数据 // 保存前校验所有步骤的数据
const result = await validateAllSteps(); const result = await validateAllSteps();
@@ -311,10 +311,10 @@ const handleSave = async () => {
console.error('保存失败:', error); console.error('保存失败:', error);
// message.warning(error.msg || '请完善所有步骤的必填信息'); // message.warning(error.msg || '请完善所有步骤的必填信息');
} }
}; }
/** 发布操作 */ /** 发布操作 */
const handleDeploy = async () => { async function handleDeploy() {
try { try {
// 修改场景下直接发布,新增场景下需要先确认 // 修改场景下直接发布,新增场景下需要先确认
if (!formData.value.id) { if (!formData.value.id) {
@@ -344,10 +344,10 @@ const handleDeploy = async () => {
console.error('发布失败:', error); console.error('发布失败:', error);
message.warning(error.message || '发布失败'); message.warning(error.message || '发布失败');
} }
}; }
/** 步骤切换处理 */ /** 步骤切换处理 */
const handleStepClick = async (index: number) => { async function handleStepClick(index: number) {
try { try {
if (index !== 0) { if (index !== 0) {
await validateBasic(); await validateBasic();
@@ -369,17 +369,17 @@ const handleStepClick = async (index: number) => {
message.warning('请先完善当前步骤必填信息'); message.warning('请先完善当前步骤必填信息');
} }
} }
}; }
const tabs = useTabs(); const tabs = useTabs();
/** 返回列表页 */ /** 返回列表页 */
const handleBack = () => { function handleBack() {
// 关闭当前页签 // 关闭当前页签
tabs.closeCurrentTab(); tabs.closeCurrentTab();
// 跳转到列表页,使用路径, 目前后端的路由 name 'name'+ menuId // 跳转到列表页,使用路径, 目前后端的路由 name 'name'+ menuId
router.push({ path: '/bpm/manager/model' }); router.push({ path: '/bpm/manager/model' });
}; }
/** 初始化 */ /** 初始化 */
onMounted(async () => { onMounted(async () => {
@@ -397,11 +397,9 @@ onBeforeUnmount(() => {
<template> <template>
<Page auto-content-height> <Page auto-content-height>
<div class="mx-auto"> <!-- 主体内容 -->
<!-- 头部导航栏 --> <Card class="mb-4">
<div <template #title>
class="absolute inset-x-0 top-0 z-10 flex h-12 items-center border-b bg-white px-5"
>
<!-- 左侧标题 --> <!-- 左侧标题 -->
<div class="flex w-[200px] items-center overflow-hidden"> <div class="flex w-[200px] items-center overflow-hidden">
<ArrowLeft <ArrowLeft
@@ -415,7 +413,20 @@ onBeforeUnmount(() => {
{{ formData.name || '创建流程' }} {{ formData.name || '创建流程' }}
</span> </span>
</div> </div>
</template>
<template #extra>
<Button
v-if="actionType === 'update'"
type="primary"
@click="handleDeploy"
>
</Button>
<Button type="primary" @click="handleSave">
<span v-if="actionType === 'definition'"> </span>
<span v-else> </span>
</Button>
</template>
<!-- 步骤条 --> <!-- 步骤条 -->
<div class="flex h-full flex-1 items-center justify-center"> <div class="flex h-full flex-1 items-center justify-center">
<div class="flex h-full w-auto items-center justify-center"> <div class="flex h-full w-auto items-center justify-center">
@@ -446,24 +457,6 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</div> </div>
<!-- 右侧按钮 -->
<div class="flex w-[200px] items-center justify-end gap-2">
<Button
v-if="actionType === 'update'"
type="primary"
@click="handleDeploy"
>
</Button>
<Button type="primary" @click="handleSave">
<span v-if="actionType === 'definition'"> </span>
<span v-else> </span>
</Button>
</div>
</div>
<!-- 主体内容 -->
<Card :body-style="{ padding: '10px' }" class="mb-4">
<div class="mt-[50px]"> <div class="mt-[50px]">
<!-- 第一步基本信息 --> <!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-4/6"> <div v-if="currentStep === 0" class="mx-auto w-4/6">
@@ -497,6 +490,5 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</Card> </Card>
</div>
</Page> </Page>
</template> </template>

View File

@@ -10,6 +10,7 @@ import type { SystemUserApi } from '#/api/system/user';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { CircleHelp, IconifyIcon, Plus, X } from '@vben/icons'; import { CircleHelp, IconifyIcon, Plus, X } from '@vben/icons';
import { import {
@@ -41,6 +42,16 @@ const props = defineProps({
}, },
}); });
const [UserSelectModalComp, userSelectModalApi] = useVbenModal({
connectedComponent: UserSelectModal,
destroyOnClose: true,
});
const [DeptSelectModalComp, deptSelectModalApi] = useVbenModal({
connectedComponent: DeptSelectModal,
destroyOnClose: true,
});
// 表单引用 // 表单引用
const formRef = ref(); const formRef = ref();
@@ -52,8 +63,6 @@ const selectedStartDepts = ref<SystemDeptApi.Dept[]>([]);
// 选中的流程管理员 // 选中的流程管理员
const selectedManagerUsers = ref<SystemUserApi.User[]>([]); const selectedManagerUsers = ref<SystemUserApi.User[]>([]);
const userSelectFormRef = ref();
const deptSelectFormRef = ref();
const currentSelectType = ref<'manager' | 'start'>('start'); const currentSelectType = ref<'manager' | 'start'>('start');
// 选中的用户 // 选中的用户
const selectedUsers = ref<number[]>(); const selectedUsers = ref<number[]>();
@@ -98,37 +107,37 @@ watch(
); );
/** 打开发起人选择 */ /** 打开发起人选择 */
const openStartUserSelect = () => { function openStartUserSelect() {
currentSelectType.value = 'start'; currentSelectType.value = 'start';
selectedUsers.value = selectedStartUsers.value.map( selectedUsers.value = selectedStartUsers.value.map(
(user) => user.id, (user) => user.id,
) as number[]; ) as number[];
userSelectFormRef.value.open(selectedUsers.value); userSelectModalApi.setData({ userIds: selectedUsers.value }).open();
}; }
/** 打开部门选择 */ /** 打开部门选择 */
const openStartDeptSelect = () => { function openStartDeptSelect() {
deptSelectFormRef.value.open(selectedStartDepts.value); deptSelectModalApi.setData({ selectedList: selectedStartDepts.value }).open();
}; }
/** 处理部门选择确认 */ /** 处理部门选择确认 */
const handleDeptSelectConfirm = (depts: SystemDeptApi.Dept[]) => { function handleDeptSelectConfirm(depts: SystemDeptApi.Dept[]) {
modelData.value = { modelData.value = {
...modelData.value, ...modelData.value,
startDeptIds: depts.map((d) => d.id), startDeptIds: depts.map((d) => d.id),
}; };
}; }
/** 打开管理员选择 */ /** 打开管理员选择 */
const openManagerUserSelect = () => { function openManagerUserSelect() {
currentSelectType.value = 'manager'; currentSelectType.value = 'manager';
selectedUsers.value = selectedManagerUsers.value.map( selectedUsers.value = selectedManagerUsers.value.map(
(user) => user.id, (user) => user.id,
) as number[]; ) as number[];
userSelectFormRef.value.open(selectedUsers.value); userSelectModalApi.setData({ userIds: selectedUsers.value }).open();
}; }
/** 处理用户选择确认 */ /** 处理用户选择确认 */
const handleUserSelectConfirm = (userList: SystemUserApi.User[]) => { function handleUserSelectConfirm(userList: SystemUserApi.User[]) {
modelData.value = modelData.value =
currentSelectType.value === 'start' currentSelectType.value === 'start'
? { ? {
@@ -139,20 +148,20 @@ const handleUserSelectConfirm = (userList: SystemUserApi.User[]) => {
...modelData.value, ...modelData.value,
managerUserIds: userList.map((u) => u.id), managerUserIds: userList.map((u) => u.id),
}; };
}; }
/** 用户选择弹窗关闭 */ /** 用户选择弹窗关闭 */
const handleUserSelectClosed = () => { function handleUserSelectClosed() {
selectedUsers.value = []; selectedUsers.value = [];
}; }
/** 用户选择弹窗取消 */ /** 用户选择弹窗取消 */
const handleUserSelectCancel = () => { function handleUserSelectCancel() {
selectedUsers.value = []; selectedUsers.value = [];
}; }
/** 处理发起人类型变化 */ /** 处理发起人类型变化 */
const handleStartUserTypeChange = (value: SelectValue) => { function handleStartUserTypeChange(value: SelectValue) {
const numValue = Number(value); const numValue = Number(value);
switch (numValue) { switch (numValue) {
case 0: { case 0: {
@@ -181,47 +190,48 @@ const handleStartUserTypeChange = (value: SelectValue) => {
break; break;
} }
} }
}; }
/** 移除发起人 */ /** 移除发起人 */
const handleRemoveStartUser = (user: SystemUserApi.User) => { function handleRemoveStartUser(user: SystemUserApi.User) {
modelData.value = { modelData.value = {
...modelData.value, ...modelData.value,
startUserIds: modelData.value.startUserIds.filter( startUserIds: modelData.value.startUserIds.filter(
(id: number) => id !== user.id, (id: number) => id !== user.id,
), ),
}; };
}; }
/** 移除部门 */ /** 移除部门 */
const handleRemoveStartDept = (dept: SystemDeptApi.Dept) => { function handleRemoveStartDept(dept: SystemDeptApi.Dept) {
modelData.value = { modelData.value = {
...modelData.value, ...modelData.value,
startDeptIds: modelData.value.startDeptIds.filter( startDeptIds: modelData.value.startDeptIds.filter(
(id: number) => id !== dept.id, (id: number) => id !== dept.id,
), ),
}; };
}; }
/** 移除管理员 */ /** 移除管理员 */
const handleRemoveManagerUser = (user: SystemUserApi.User) => { function handleRemoveManagerUser(user: SystemUserApi.User) {
modelData.value = { modelData.value = {
...modelData.value, ...modelData.value,
managerUserIds: modelData.value.managerUserIds.filter( managerUserIds: modelData.value.managerUserIds.filter(
(id: number) => id !== user.id, (id: number) => id !== user.id,
), ),
}; };
}; }
/** 表单校验 */ /** 表单校验 */
const validate = async () => { async function validate() {
await formRef.value?.validate(); await formRef.value?.validate();
}; }
defineExpose({ validate }); defineExpose({ validate });
</script> </script>
<template> <template>
<div>
<Form <Form
ref="formRef" ref="formRef"
:model="modelData" :model="modelData"
@@ -321,7 +331,7 @@ defineExpose({ validate });
<div <div
v-for="user in selectedStartUsers" v-for="user in selectedStartUsers"
:key="user.id" :key="user.id"
class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2" class="bg-destructive text-destructive-foreground hover:bg-destructive-hover relative flex h-9 items-center rounded-full pr-2"
> >
<Avatar <Avatar
class="m-1" class="m-1"
@@ -334,7 +344,7 @@ defineExpose({ validate });
</Avatar> </Avatar>
{{ user.nickname }} {{ user.nickname }}
<X <X
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer"
@click="handleRemoveStartUser(user)" @click="handleRemoveStartUser(user)"
/> />
</div> </div>
@@ -344,7 +354,10 @@ defineExpose({ validate });
class="flex items-center" class="flex items-center"
> >
<template #icon> <template #icon>
<IconifyIcon icon="mdi:account-plus-outline" class="size-[18px]" /> <IconifyIcon
icon="mdi:account-plus-outline"
class="size-[18px]"
/>
</template> </template>
选择人员 选择人员
</Button> </Button>
@@ -356,12 +369,12 @@ defineExpose({ validate });
<div <div
v-for="dept in selectedStartDepts" v-for="dept in selectedStartDepts"
:key="dept.id" :key="dept.id"
class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2" class="bg-destructive text-destructive-foreground hover:bg-destructive-hover relative flex h-9 items-center rounded-full pr-2 shadow-sm"
> >
<IconifyIcon icon="ep:office-building" class="size-6 px-1" /> <IconifyIcon icon="ep:office-building" class="size-6 px-1" />
{{ dept.name }} {{ dept.name }}
<X <X
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer"
@click="handleRemoveStartDept(dept)" @click="handleRemoveStartDept(dept)"
/> />
</div> </div>
@@ -382,7 +395,7 @@ defineExpose({ validate });
<div <div
v-for="user in selectedManagerUsers" v-for="user in selectedManagerUsers"
:key="user.id" :key="user.id"
class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2" class="bg-destructive text-destructive-foreground hover:bg-destructive-hover relative flex h-9 items-center rounded-full pr-2"
> >
<Avatar <Avatar
class="m-1" class="m-1"
@@ -395,7 +408,7 @@ defineExpose({ validate });
</Avatar> </Avatar>
{{ user.nickname }} {{ user.nickname }}
<X <X
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer"
@click="handleRemoveManagerUser(user)" @click="handleRemoveManagerUser(user)"
/> />
</div> </div>
@@ -405,7 +418,10 @@ defineExpose({ validate });
class="flex items-center" class="flex items-center"
> >
<template #icon> <template #icon>
<IconifyIcon icon="mdi:account-plus-outline" class="size-[18px]" /> <IconifyIcon
icon="mdi:account-plus-outline"
class="size-[18px]"
/>
</template> </template>
选择人员 选择人员
</Button> </Button>
@@ -414,8 +430,7 @@ defineExpose({ validate });
</Form> </Form>
<!-- 用户选择弹窗 --> <!-- 用户选择弹窗 -->
<UserSelectModal <UserSelectModalComp
ref="userSelectFormRef"
v-model:value="selectedUsers" v-model:value="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户" title="选择用户"
@@ -424,27 +439,17 @@ defineExpose({ validate });
@cancel="handleUserSelectCancel" @cancel="handleUserSelectCancel"
/> />
<!-- 部门选择对话框 --> <!-- 部门选择对话框 -->
<DeptSelectModal <DeptSelectModalComp
ref="deptSelectFormRef"
title="发起人部门选择" title="发起人部门选择"
:check-strictly="true" :check-strictly="true"
@confirm="handleDeptSelectConfirm" @confirm="handleDeptSelectConfirm"
/> />
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.bg-gray-100 {
background-color: #f5f7fa;
transition: all 0.3s;
&:hover {
background-color: #e6e8eb;
}
}
.upload-img-placeholder { .upload-img-placeholder {
cursor: pointer; cursor: pointer;
background-color: #fafafa;
transition: all 0.3s; transition: all 0.3s;
&:hover { &:hover {

View File

@@ -91,9 +91,9 @@ const numberExample = computed(() => {
/** 是否开启流程前置通知 */ /** 是否开启流程前置通知 */
const processBeforeTriggerEnable = ref(false); const processBeforeTriggerEnable = ref(false);
const handleProcessBeforeTriggerEnableChange = ( function handleProcessBeforeTriggerEnableChange(
val: boolean | number | string, val: boolean | number | string,
) => { ) {
modelData.value.processBeforeTriggerSetting = val modelData.value.processBeforeTriggerSetting = val
? { ? {
url: '', url: '',
@@ -102,13 +102,11 @@ const handleProcessBeforeTriggerEnableChange = (
response: [], response: [],
} }
: null; : null;
}; }
/** 是否开启流程后置通知 */ /** 是否开启流程后置通知 */
const processAfterTriggerEnable = ref(false); const processAfterTriggerEnable = ref(false);
const handleProcessAfterTriggerEnableChange = ( function handleProcessAfterTriggerEnableChange(val: boolean | number | string) {
val: boolean | number | string,
) => {
modelData.value.processAfterTriggerSetting = val modelData.value.processAfterTriggerSetting = val
? { ? {
url: '', url: '',
@@ -117,13 +115,11 @@ const handleProcessAfterTriggerEnableChange = (
response: [], response: [],
} }
: null; : null;
}; }
/** 是否开启任务前置通知 */ /** 是否开启任务前置通知 */
const taskBeforeTriggerEnable = ref(false); const taskBeforeTriggerEnable = ref(false);
const handleTaskBeforeTriggerEnableChange = ( function handleTaskBeforeTriggerEnableChange(val: boolean | number | string) {
val: boolean | number | string,
) => {
modelData.value.taskBeforeTriggerSetting = val modelData.value.taskBeforeTriggerSetting = val
? { ? {
url: '', url: '',
@@ -132,11 +128,11 @@ const handleTaskBeforeTriggerEnableChange = (
response: [], response: [],
} }
: null; : null;
}; }
/** 是否开启任务后置通知 */ /** 是否开启任务后置通知 */
const taskAfterTriggerEnable = ref(false); const taskAfterTriggerEnable = ref(false);
const handleTaskAfterTriggerEnableChange = (val: boolean | number | string) => { function handleTaskAfterTriggerEnableChange(val: boolean | number | string) {
modelData.value.taskAfterTriggerSetting = val modelData.value.taskAfterTriggerSetting = val
? { ? {
url: '', url: '',
@@ -145,7 +141,7 @@ const handleTaskAfterTriggerEnableChange = (val: boolean | number | string) => {
response: [], response: [],
} }
: null; : null;
}; }
/** 表单选项 */ /** 表单选项 */
const formField = ref<Array<{ field: string; title: string }>>([]); const formField = ref<Array<{ field: string; title: string }>>([]);
@@ -181,7 +177,7 @@ const formFieldOptions4Summary = computed(() => {
}); });
/** 兼容以前未配置更多设置的流程 */ /** 兼容以前未配置更多设置的流程 */
const initData = () => { function initData() {
if (!modelData.value.processIdRule) { if (!modelData.value.processIdRule) {
modelData.value.processIdRule = { modelData.value.processIdRule = {
enable: false, enable: false,
@@ -218,7 +214,7 @@ const initData = () => {
if (modelData.value.taskAfterTriggerSetting) { if (modelData.value.taskAfterTriggerSetting) {
taskAfterTriggerEnable.value = true; taskAfterTriggerEnable.value = true;
} }
}; }
/** 监听表单 ID 变化,加载表单数据 */ /** 监听表单 ID 变化,加载表单数据 */
watch( watch(
@@ -242,9 +238,9 @@ watch(
// 表单引用 // 表单引用
const formRef = ref(); const formRef = ref();
/** 表单校验 */ /** 表单校验 */
const validate = async () => { async function validate() {
await formRef.value?.validate(); await formRef.value?.validate();
}; }
defineExpose({ initData, validate }); defineExpose({ initData, validate });
</script> </script>

View File

@@ -80,9 +80,9 @@ const rules: Record<string, Rule[]> = {
}; };
/** 表单校验 */ /** 表单校验 */
const validate = async () => { async function validate() {
await formRef.value?.validate(); await formRef.value?.validate();
}; }
defineExpose({ validate }); defineExpose({ validate });
</script> </script>

View File

@@ -16,7 +16,7 @@ const processData = inject('processData') as Ref;
const simpleDesign = ref(); const simpleDesign = ref();
/** 表单校验 */ /** 表单校验 */
const validate = async () => { async function validate() {
// 获取最新的流程数据 // 获取最新的流程数据
if (!processData.value) { if (!processData.value) {
throw new Error('请设计流程'); throw new Error('请设计流程');
@@ -29,9 +29,9 @@ const validate = async () => {
} }
} }
return true; return true;
}; }
/** 处理设计器保存成功 */ /** 处理设计器保存成功 */
const handleDesignSuccess = async (data?: any) => { async function handleDesignSuccess(data?: any) {
if (data) { if (data) {
// 创建新的对象以触发响应式更新 // 创建新的对象以触发响应式更新
const newModelData = { const newModelData = {
@@ -44,7 +44,7 @@ const handleDesignSuccess = async (data?: any) => {
// 更新表单的模型数据部分 // 更新表单的模型数据部分
modelData.value = newModelData; modelData.value = newModelData;
} }
}; }
/** 是否显示设计器 */ /** 是否显示设计器 */
const showDesigner = computed(() => { const showDesigner = computed(() => {

View File

@@ -18,15 +18,15 @@ const emit = defineEmits(['success']);
const designerRef = ref(); const designerRef = ref();
/** 保存成功回调 */ /** 保存成功回调 */
const handleSuccess = (data?: any) => { function handleSuccess(data?: any) {
if (data) { if (data) {
emit('success', data); emit('success', data);
} }
}; }
/** 设计器配置校验 */ /** 设计器配置校验 */
const validateConfig = async () => { async function validateConfig() {
return await designerRef.value.validate(); return await designerRef.value.validate();
}; }
defineExpose({ validateConfig }); defineExpose({ validateConfig });
</script> </script>
<template> <template>

View File

@@ -4,21 +4,12 @@ import type { ModelCategoryInfo } from '#/api/bpm/model';
import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue'; import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { Plus, Search, Settings } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { cloneDeep } from '@vben/utils'; import { cloneDeep } from '@vben/utils';
import { refAutoReset } from '@vueuse/core'; import { refAutoReset } from '@vueuse/core';
import { useSortable } from '@vueuse/integrations/useSortable'; import { useSortable } from '@vueuse/integrations/useSortable';
import { import { Button, Card, Dropdown, Input, Menu, message } from 'ant-design-vue';
Button,
Card,
Divider,
Dropdown,
Form,
Input,
Menu,
message,
} from 'ant-design-vue';
import { import {
getCategorySimpleList, getCategorySimpleList,
@@ -72,7 +63,7 @@ watch(
); );
/** 加载数据 */ /** 加载数据 */
const getList = async () => { async function getList() {
modelListSpinning.value = true; modelListSpinning.value = true;
try { try {
const modelList = await getModelList(queryParams.name); const modelList = await getModelList(queryParams.name);
@@ -89,27 +80,22 @@ const getList = async () => {
} finally { } finally {
modelListSpinning.value = false; modelListSpinning.value = false;
} }
}; }
/** 初始化 */ /** 初始化 */
onActivated(() => { onActivated(() => {
getList(); getList();
}); });
/** 查询方法 */
const handleQuery = () => {
getList();
};
/** 新增模型 */ /** 新增模型 */
const createModel = () => { function createModel() {
router.push({ router.push({
name: 'BpmModelCreate', name: 'BpmModelCreate',
}); });
}; }
/** 处理下拉菜单命令 */ /** 处理下拉菜单命令 */
const handleCommand = (command: string) => { function handleCommand(command: string) {
if (command === 'handleCategoryAdd') { if (command === 'handleCategoryAdd') {
// 打开新建流程分类弹窗 // 打开新建流程分类弹窗
categoryFormModalApi.open(); categoryFormModalApi.open();
@@ -126,10 +112,10 @@ const handleCommand = (command: string) => {
}); });
} }
} }
}; }
/** 取消分类排序 */ /** 取消分类排序 */
const handleCategorySortCancel = () => { function handleCategorySortCancel() {
// 恢复初始数据 // 恢复初始数据
categoryGroup.value = cloneDeep(originalData.value); categoryGroup.value = cloneDeep(originalData.value);
isCategorySorting.value = false; isCategorySorting.value = false;
@@ -137,10 +123,10 @@ const handleCategorySortCancel = () => {
if (sortableInstance.value) { if (sortableInstance.value) {
sortableInstance.value.option('disabled', true); sortableInstance.value.option('disabled', true);
} }
}; }
/** 提交分类排序 */ /** 提交分类排序 */
const handleCategorySortSubmit = async () => { async function handleCategorySortSubmit() {
saveSortLoading.value = true; saveSortLoading.value = true;
try { try {
// 保存排序逻辑 // 保存排序逻辑
@@ -157,76 +143,56 @@ const handleCategorySortSubmit = async () => {
if (sortableInstance.value) { if (sortableInstance.value) {
sortableInstance.value.option('disabled', true); sortableInstance.value.option('disabled', true);
} }
}; }
</script> </script>
<template> <template>
<Page auto-content-height> <Page auto-content-height>
<!-- TODO @jaosn没头像的图标展示文字头像哈 @芋艿 好像已经展示了文字头像是模型列表中吗? -->
<!-- 流程分类表单弹窗 --> <!-- 流程分类表单弹窗 -->
<CategoryFormModal @success="getList" /> <CategoryFormModal @success="getList" />
<Card <Card
:body-style="{ padding: '10px' }" :body-style="{ padding: '10px' }"
class="mb-4" class="mb-4"
title="流程模型"
v-spinning="modelListSpinning" v-spinning="modelListSpinning"
> >
<div class="flex h-full items-center justify-between pl-5"> <template #extra>
<span class="-mb-4 text-lg font-extrabold">流程模型</span>
<!-- 搜索工作栏 -->
<Form
v-if="!isCategorySorting"
class="-mb-4 mr-2.5 flex"
:model="queryParams"
layout="inline"
>
<Form.Item name="name" class="ml-auto">
<Input <Input
v-model:value="queryParams.name" v-model:value="queryParams.name"
placeholder="搜索流程" placeholder="搜索流程"
allow-clear allow-clear
@press-enter="handleQuery" @press-enter="getList"
class="!w-60" class="!w-60"
> />
<template #prefix> <Button class="ml-2" type="primary" @click="createModel">
<Search class="mx-2.5" /> <IconifyIcon icon="lucide:plus" /> 新建模型
</template>
</Input>
</Form.Item>
<!-- 右上角新建模型更多操作 -->
<Form.Item>
<Button type="primary" @click="createModel">
<Plus class="size-5" /> 新建模型
</Button> </Button>
</Form.Item> <Dropdown class="ml-2" placement="bottomRight" arrow>
<Form.Item>
<Dropdown placement="bottomRight" arrow>
<Button> <Button>
<template #icon> <template #icon>
<Settings class="size-4" /> <IconifyIcon icon="lucide:settings" />
</template> </template>
</Button> </Button>
<template #overlay> <template #overlay>
<Menu @click="(e) => handleCommand(e.key as string)"> <Menu @click="(e) => handleCommand(e.key as string)">
<Menu.Item key="handleCategoryAdd"> <Menu.Item key="handleCategoryAdd">
<div class="flex items-center"> <div class="flex items-center">
<span <IconifyIcon icon="lucide:plus" />
class="icon-[ant-design--plus-outlined] mr-1.5 text-[18px]"
></span>
新建分类 新建分类
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item key="handleCategorySort"> <Menu.Item key="handleCategorySort">
<div class="flex items-center"> <div class="flex items-center">
<span class="icon-[fa--sort-amount-desc] mr-1.5"></span> <IconifyIcon icon="lucide:align-start-vertical" />
分类排序 分类排序
</div> </div>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</template> </template>
</Dropdown> </Dropdown>
</Form.Item> </template>
</Form> <div class="flex h-full items-center justify-between pl-5">
<div class="-mb-4 mr-6" v-else> <div class="mb-4 mr-6" v-if="isCategorySorting">
<Button @click="handleCategorySortCancel" class="mr-3"> <Button @click="handleCategorySortCancel" class="mr-3">
</Button> </Button>
@@ -240,9 +206,8 @@ const handleCategorySortSubmit = async () => {
</div> </div>
</div> </div>
<Divider />
<!-- 按照分类展示其所属的模型列表 --> <!-- 按照分类展示其所属的模型列表 -->
<div class="px-5" ref="categoryGroupRef"> <div class="px-3" ref="categoryGroupRef">
<CategoryDraggableModel <CategoryDraggableModel
v-for="element in categoryGroup" v-for="element in categoryGroup"
:class="isCategorySorting ? 'cursor-move' : ''" :class="isCategorySorting ? 'cursor-move' : ''"

View File

@@ -5,6 +5,7 @@ import { computed, ref, watchEffect } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { confirm, useVbenModal } from '@vben/common-ui'; import { confirm, useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { useUserStore } from '@vben/stores'; import { useUserStore } from '@vben/stores';
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils'; import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
@@ -38,6 +39,12 @@ const props = defineProps<{
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
// 重命名分类对话框
const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({
connectedComponent: CategoryRenameForm,
destroyOnClose: true,
});
const router = useRouter(); const router = useRouter();
// 获取当前登录用户Id // 获取当前登录用户Id
const userStore = useUserStore(); const userStore = useUserStore();
@@ -104,7 +111,7 @@ const columns = [
]; ];
/** 处理模型的排序 */ /** 处理模型的排序 */
const handleModelSort = () => { function handleModelSort() {
// 保存初始数据 // 保存初始数据
originalData.value = cloneDeep(props.categoryInfo.modelList); originalData.value = cloneDeep(props.categoryInfo.modelList);
// 展开数据 // 展开数据
@@ -120,10 +127,10 @@ const handleModelSort = () => {
disabled: false, // 启用排序 disabled: false, // 启用排序
}); });
} }
}; }
/** 处理模型的排序提交 */ /** 处理模型的排序提交 */
const handleModelSortSubmit = async () => { async function handleModelSortSubmit() {
try { try {
// 保存排序 // 保存排序
const ids = modelList.value.map((item) => item.id); const ids = modelList.value.map((item) => item.id);
@@ -135,10 +142,10 @@ const handleModelSortSubmit = async () => {
} catch (error) { } catch (error) {
console.error('排序保存失败', error); console.error('排序保存失败', error);
} }
}; }
/** 处理模型的排序取消 */ /** 处理模型的排序取消 */
const handleModelSortCancel = () => { function handleModelSortCancel() {
// 恢复初始数据 // 恢复初始数据
modelList.value = cloneDeep(originalData.value); modelList.value = cloneDeep(originalData.value);
isModelSorting.value = false; isModelSorting.value = false;
@@ -146,20 +153,20 @@ const handleModelSortCancel = () => {
if (sortableInstance.value) { if (sortableInstance.value) {
sortableInstance.value.option('disabled', true); sortableInstance.value.option('disabled', true);
} }
}; }
/** 处理下拉菜单命令 */ /** 处理下拉菜单命令 */
const handleCommand = (command: string) => { function handleCommand(command: string) {
if (command === 'renameCategory') { if (command === 'renameCategory') {
// 打开重命名分类对话框 // 打开重命名分类对话框
categoryRenameModalApi.setData(props.categoryInfo).open(); categoryRenameModalApi.setData(props.categoryInfo).open();
} else if (command === 'deleteCategory') { } else if (command === 'deleteCategory') {
handleDeleteCategory(); handleDeleteCategory();
} }
}; }
/** 删除流程分类 */ /** 删除流程分类 */
const handleDeleteCategory = async () => { async function handleDeleteCategory() {
if (props.categoryInfo.modelList.length > 0) { if (props.categoryInfo.modelList.length > 0) {
message.warning('该分类下仍有流程定义,不允许删除'); message.warning('该分类下仍有流程定义,不允许删除');
return; return;
@@ -176,13 +183,13 @@ const handleDeleteCategory = async () => {
// 刷新列表 // 刷新列表
emit('success'); emit('success');
}); });
}; }
/** 处理表单详情点击 */ /** 处理表单详情点击 */
const handleFormDetail = (row: any) => { function handleFormDetail(row: any) {
// TODO 待实现 // TODO 待实现
console.warn('待实现', row); console.warn('待实现', row);
}; }
/** 判断是否是流程管理员 */ /** 判断是否是流程管理员 */
function isManagerUser(row: any) { function isManagerUser(row: any) {
@@ -273,17 +280,11 @@ watchEffect(() => {
}); });
/** 自定义表格行渲染 */ /** 自定义表格行渲染 */
const customRow = (_record: any) => { function customRow(_record: any) {
return { return {
class: isModelSorting.value ? 'cursor-move' : '', class: isModelSorting.value ? 'cursor-move' : '',
}; };
}; }
// 重命名分类对话框
const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({
connectedComponent: CategoryRenameForm,
destroyOnClose: true,
});
// 处理重命名成功 // 处理重命名成功
const handleRenameSuccess = () => { const handleRenameSuccess = () => {
@@ -292,6 +293,7 @@ const handleRenameSuccess = () => {
</script> </script>
<template> <template>
<div>
<Card <Card
:body-style="{ padding: 0 }" :body-style="{ padding: 0 }"
class="category-draggable-model mb-5 rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl" class="category-draggable-model mb-5 rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
@@ -304,7 +306,9 @@ const handleRenameSuccess = () => {
class="icon-[ic--round-drag-indicator] ml-2.5 cursor-move text-2xl text-gray-500" class="icon-[ic--round-drag-indicator] ml-2.5 cursor-move text-2xl text-gray-500"
></span> ></span>
</Tooltip> </Tooltip>
<div class="ml-4 mr-2 text-lg font-medium">{{ categoryInfo.name }}</div> <div class="ml-4 mr-2 text-lg font-medium">
{{ categoryInfo.name }}
</div>
<div class="text-gray-500"> <div class="text-gray-500">
({{ categoryInfo.modelList?.length || 0 }}) ({{ categoryInfo.modelList?.length || 0 }})
</div> </div>
@@ -336,7 +340,7 @@ const handleRenameSuccess = () => {
@click.stop="handleModelSort" @click.stop="handleModelSort"
> >
<template #icon> <template #icon>
<span class="icon-[fa--sort-amount-desc]"></span> <IconifyIcon icon="lucide:align-start-vertical" />
</template> </template>
排序 排序
</Button> </Button>
@@ -347,7 +351,7 @@ const handleRenameSuccess = () => {
class="flex items-center text-[14px]" class="flex items-center text-[14px]"
> >
<template #icon> <template #icon>
<span class="icon-[ant-design--setting-outlined]"></span> <IconifyIcon icon="lucide:settings" />
</template> </template>
分类 分类
</Button> </Button>
@@ -373,7 +377,11 @@ const handleRenameSuccess = () => {
</div> </div>
<!-- 模型列表 --> <!-- 模型列表 -->
<Collapse :active-key="expandKeys" :bordered="false" class="bg-transparent"> <Collapse
:active-key="expandKeys"
:bordered="false"
class="bg-transparent"
>
<Collapse.Panel <Collapse.Panel
key="1" key="1"
:show-arrow="false" :show-arrow="false"
@@ -404,9 +412,9 @@ const handleRenameSuccess = () => {
v-if="!record.icon" v-if="!record.icon"
class="mr-2.5 flex h-9 w-9 items-center justify-center rounded bg-blue-500 text-white" class="mr-2.5 flex h-9 w-9 items-center justify-center rounded bg-blue-500 text-white"
> >
<span style="font-size: 12px">{{ <span style="font-size: 12px">
record.name.substring(0, 2) {{ record.name.substring(0, 2) }}
}}</span> </span>
</div> </div>
<img <img
v-else v-else
@@ -421,7 +429,9 @@ const handleRenameSuccess = () => {
<!-- 可见范围列--> <!-- 可见范围列-->
<template v-else-if="column.key === 'startUserIds'"> <template v-else-if="column.key === 'startUserIds'">
<span <span
v-if="!record.startUsers?.length && !record.startDepts?.length" v-if="
!record.startUsers?.length && !record.startDepts?.length
"
> >
全部可见 全部可见
</span> </span>
@@ -460,7 +470,10 @@ const handleRenameSuccess = () => {
<template v-else-if="column.key === 'type'"> <template v-else-if="column.key === 'type'">
<!-- <DictTag :value="record.type" :type="DICT_TYPE.BPM_MODEL_TYPE" /> --> <!-- <DictTag :value="record.type" :type="DICT_TYPE.BPM_MODEL_TYPE" /> -->
<!-- <Tag>{{ record.type }}</Tag> --> <!-- <Tag>{{ record.type }}</Tag> -->
<DictTag :type="DICT_TYPE.BPM_MODEL_TYPE" :value="record.type" /> <DictTag
:type="DICT_TYPE.BPM_MODEL_TYPE"
:value="record.type"
/>
</template> </template>
<!-- 表单信息列 --> <!-- 表单信息列 -->
<template v-else-if="column.key === 'formType'"> <template v-else-if="column.key === 'formType'">
@@ -486,7 +499,9 @@ const handleRenameSuccess = () => {
<template v-else-if="column.key === 'deploymentTime'"> <template v-else-if="column.key === 'deploymentTime'">
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<span v-if="record.processDefinition" class="w-[150px]"> <span v-if="record.processDefinition" class="w-[150px]">
{{ formatDateTime(record.processDefinition.deploymentTime) }} {{
formatDateTime(record.processDefinition.deploymentTime)
}}
</span> </span>
<Tag v-if="record.processDefinition"> <Tag v-if="record.processDefinition">
v{{ record.processDefinition.version }} v{{ record.processDefinition.version }}
@@ -578,6 +593,7 @@ const handleRenameSuccess = () => {
<!-- 重命名分类弹窗 --> <!-- 重命名分类弹窗 -->
<CategoryRenameModal @success="handleRenameSuccess" /> <CategoryRenameModal @success="handleRenameSuccess" />
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -152,7 +152,7 @@ function onBack() {
// ============================== 审核流程相关 ============================== // ============================== 审核流程相关 ==============================
/** 审批相关:获取审批详情 */ /** 审批相关:获取审批详情 */
const getApprovalDetail = async () => { async function getApprovalDetail() {
try { try {
const data = await getApprovalDetailApi({ const data = await getApprovalDetailApi({
processDefinitionId: processDefinitionId.value, processDefinitionId: processDefinitionId.value,
@@ -188,13 +188,12 @@ const getApprovalDetail = async () => {
: []; : [];
} }
} }
} finally { } finally {}
} }
};
/** 审批相关:选择发起人 */ /** 审批相关:选择发起人 */
const selectUserConfirm = (id: string, userList: any[]) => { function selectUserConfirm(id: string, userList: any[]) {
startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id); startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id);
}; }
/** 审批相关:预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次, formData.value可改成实际业务中的特定字段 */ /** 审批相关:预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次, formData.value可改成实际业务中的特定字段 */
watch( watch(

View File

@@ -1,18 +1,14 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmCategoryApi } from '#/api/bpm/category';
import type { DescriptionItemSchema } from '#/components/description'; import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { useAccess } from '@vben/access';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils'; import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -118,9 +114,7 @@ export function GridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmCategoryApi.CategoryVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'id', field: 'id',
@@ -168,39 +162,11 @@ export function useGridColumns<T = BpmCategoryApi.CategoryVO>(
minWidth: 180, minWidth: 180,
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 180, width: 180,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '请假',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['bpm:oa-leave:query']),
},
{
code: 'progress',
text: '进度',
show: hasAccessByCodes(['bpm:oa-leave:query']),
},
{
code: 'cancel',
text: '取消',
show: (row: any) =>
row.status === 1 && hasAccessByCodes(['bpm:oa-leave:query']),
},
],
},
}, },
]; ];
} }

View File

@@ -19,14 +19,14 @@ const detailData = ref<BpmOALeaveApi.LeaveVO>();
const { query } = useRoute(); const { query } = useRoute();
const queryId = computed(() => query.id as string); const queryId = computed(() => query.id as string);
const getDetailData = async () => { async function getDetailData() {
try { try {
datailLoading.value = true; datailLoading.value = true;
detailData.value = await getLeave(Number(props.id || queryId.value)); detailData.value = await getLeave(Number(props.id || queryId.value));
} finally { } finally {
datailLoading.value = false; datailLoading.value = false;
} }
}; }
onMounted(() => { onMounted(() => {
getDetailData(); getDetailData();

View File

@@ -1,20 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PageParam } from '@vben/request'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { BpmOALeaveApi } from '#/api/bpm/oa/leave'; import type { BpmOALeaveApi } from '#/api/bpm/oa/leave';
import { h } from 'vue'; import { h } from 'vue';
import { Page, prompt } from '@vben/common-ui'; import { Page, prompt } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message, Textarea } from 'ant-design-vue'; import { message, Textarea } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getLeavePage } from '#/api/bpm/oa/leave'; import { getLeavePage } from '#/api/bpm/oa/leave';
import { cancelProcessInstanceByStartUser } from '#/api/bpm/processInstance'; import { cancelProcessInstanceByStartUser } from '#/api/bpm/processInstance';
import { DocAlert } from '#/components/doc-alert'; import { DocAlert } from '#/components/doc-alert';
@@ -27,12 +21,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: GridFormSchema(), schema: GridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(onActionClick), columns: useGridColumns(),
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }: PageParam, formValues: any) => { query: async ({ page }, formValues) => {
return await getLeavePage({ return await getLeavePage({
pageNo: page.currentPage, pageNo: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
@@ -52,7 +46,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
}); });
/** 创建请假 */ /** 创建请假 */
function onCreate() { function handleCreate() {
router.push({ router.push({
name: 'OALeaveCreate', name: 'OALeaveCreate',
query: { query: {
@@ -62,15 +56,15 @@ function onCreate() {
} }
/** 查看请假详情 */ /** 查看请假详情 */
const onDetail = (row: BpmOALeaveApi.LeaveVO) => { function handleDetail(row: BpmOALeaveApi.LeaveVO) {
router.push({ router.push({
name: 'OALeaveDetail', name: 'OALeaveDetail',
query: { id: row.id }, query: { id: row.id },
}); });
}; }
/** 取消请假 */ /** 取消请假 */
const onCancel = (row: BpmOALeaveApi.LeaveVO) => { function handleCancel(row: BpmOALeaveApi.LeaveVO) {
prompt({ prompt({
async beforeClose(scope) { async beforeClose(scope) {
if (scope.isConfirm) { if (scope.isConfirm) {
@@ -100,35 +94,14 @@ const onCancel = (row: BpmOALeaveApi.LeaveVO) => {
title: '取消流程', title: '取消流程',
modelPropName: 'value', modelPropName: 'value',
}); });
}; }
/** 审批进度 */ /** 审批进度 */
const onProgress = (row: BpmOALeaveApi.LeaveVO) => { function handleProgress(row: BpmOALeaveApi.LeaveVO) {
router.push({ router.push({
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
query: { id: row.processInstanceId }, query: { id: row.processInstanceId },
}); });
};
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<BpmOALeaveApi.LeaveVO>) {
switch (code) {
case 'cancel': {
onCancel(row);
break;
}
case 'detail': {
onDetail(row);
break;
}
case 'progress': {
onProgress(row);
break;
}
}
} }
/** 刷新表格 */ /** 刷新表格 */
@@ -146,25 +119,48 @@ function onRefresh() {
<Grid table-title="请假列表"> <Grid table-title="请假列表">
<template #toolbar-tools> <template #toolbar-tools>
<Button <TableAction
type="primary" :actions="[
@click="onCreate" {
v-access:code="['bpm:category:create']" label: '发起请假',
> type: 'primary',
<Plus class="size-5" /> icon: ACTION_ICON.ADD,
发起请假 auth: ['bpm:oa-leave:create'],
</Button> onClick: handleCreate,
},
]"
/>
</template> </template>
<template #actions="{ row }">
<template #userIds-cell="{ row }"> <TableAction
<span :actions="[
v-for="(userId, index) in row.userIds" {
:key="userId" label: $t('common.detail'),
class="pr-5px" type: 'link',
> icon: ACTION_ICON.VIEW,
{{ dataList.find((user) => user.id === userId)?.nickname }} auth: ['bpm:oa-leave:query'],
<span v-if="index < row.userIds.length - 1"></span> onClick: handleDetail.bind(null, row),
</span> },
{
label: '审批进度',
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['bpm:oa-leave:query'],
onClick: handleProgress.bind(null, row),
},
{
label: '取消',
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['bpm:user-group:query'],
popConfirm: {
title: '取消流程',
confirm: handleCancel.bind(null, row),
},
},
]"
/>
</template> </template>
</Grid> </Grid>
</Page> </Page>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression';
import { useAccess } from '@vben/access';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -88,9 +83,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmProcessExpressionApi.ProcessExpressionVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'id', field: 'id',
@@ -124,29 +117,10 @@ export function useGridColumns<T = BpmProcessExpressionApi.ProcessExpressionVO>(
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 180, width: 180,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '流程表达式',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['bpm:process-expression:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['bpm:process-expression:delete']),
},
],
},
}, },
]; ];
} }

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { import type { VxeTableGridOptions } from '#/adapter/vxe-table';
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression'; import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { import {
deleteProcessExpression, deleteProcessExpression,
getProcessExpressionPage, getProcessExpressionPage,
@@ -25,12 +21,46 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form, connectedComponent: Form,
destroyOnClose: true, destroyOnClose: true,
}); });
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建流程表达式 */
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑流程表达式 */
function handleEdit(row: BpmProcessExpressionApi.ProcessExpressionVO) {
formModalApi.setData(row).open();
}
/** 删除流程表达式 */
async function handleDelete(row: BpmProcessExpressionApi.ProcessExpressionVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
key: 'action_key_msg',
});
try {
await deleteProcessExpression(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} finally {
hideLoading();
}
}
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useGridColumns(onActionClick), columns: useGridColumns(),
height: 'auto', height: 'auto',
keepSource: true, keepSource: true,
proxyConfig: { proxyConfig: {
@@ -53,55 +83,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpressionVO>, } as VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpressionVO>,
}); });
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<BpmProcessExpressionApi.ProcessExpressionVO>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 刷新表格 */
function onRefresh() {
gridApi.query();
}
/** 创建流程表达式 */
function onCreate() {
formModalApi.setData(null).open();
}
/** 编辑流程表达式 */
function onEdit(row: BpmProcessExpressionApi.ProcessExpressionVO) {
formModalApi.setData(row).open();
}
/** 删除流程表达式 */
async function onDelete(row: BpmProcessExpressionApi.ProcessExpressionVO) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
});
try {
await deleteProcessExpression(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
onRefresh();
} finally {
hideLoading();
}
}
</script> </script>
<template> <template>
@@ -112,17 +93,45 @@ async function onDelete(row: BpmProcessExpressionApi.ProcessExpressionVO) {
url="https://doc.iocoder.cn/bpm/expression/" url="https://doc.iocoder.cn/bpm/expression/"
/> />
</template> </template>
<FormModal @success="onRefresh" /> <FormModal @success="onRefresh" />
<Grid table-title="流程表达式"> <Grid table-title="流程表达式">
<template #toolbar-tools> <template #toolbar-tools>
<Button <TableAction
type="primary" :actions="[
@click="onCreate" {
v-access:code="['bpm:process-expression:create']" label: $t('ui.actionTitle.create', ['流程表达式']),
> type: 'primary',
<Plus class="size-5" /> icon: ACTION_ICON.ADD,
{{ $t('ui.actionTitle.create', ['流程表达式']) }} auth: ['bpm:process-expression:create'],
</Button> onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['bpm:process-expression:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['bpm:process-expression:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template> </template>
</Grid> </Grid>
</Page> </Page>

View File

@@ -55,7 +55,6 @@ const [Modal, modalApi] = useVbenModal({
}, },
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
formData.value = undefined;
return; return;
} }
// 加载数据 // 加载数据
@@ -77,7 +76,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle" class="w-[600px]"> <Modal :title="getTitle" class="w-[40%]">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@@ -39,7 +39,7 @@ const processDefinitionList = ref<
>([]); // 流程定义的列表 >([]); // 流程定义的列表
// 实现 groupBy 功能 // 实现 groupBy 功能
const groupBy = (array: any[], key: string) => { function groupBy(array: any[], key: string) {
const result: Record<string, any[]> = {}; const result: Record<string, any[]> = {};
for (const item of array) { for (const item of array) {
const groupKey = item[key]; const groupKey = item[key];
@@ -49,10 +49,10 @@ const groupBy = (array: any[], key: string) => {
result[groupKey].push(item); result[groupKey].push(item);
} }
return result; return result;
}; }
/** 查询列表 */ /** 查询列表 */
const getList = async () => { async function getList() {
loading.value = true; loading.value = true;
try { try {
// 所有流程分类数据 // 所有流程分类数据
@@ -79,20 +79,20 @@ const getList = async () => {
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; }
/** 获取所有流程分类数据 */ /** 获取所有流程分类数据 */
const getCategoryList = async () => { async function getCategoryList() {
try { try {
// 流程分类 // 流程分类
categoryList.value = await getCategorySimpleList(); categoryList.value = await getCategorySimpleList();
} catch { } catch {
// 错误处理 // 错误处理
} }
}; }
/** 获取所有流程定义数据 */ /** 获取所有流程定义数据 */
const handleGetProcessDefinitionList = async () => { async function handleGetProcessDefinitionList() {
try { try {
// 流程定义 // 流程定义
processDefinitionList.value = await getProcessDefinitionList({ processDefinitionList.value = await getProcessDefinitionList({
@@ -108,7 +108,7 @@ const handleGetProcessDefinitionList = async () => {
} catch { } catch {
// 错误处理 // 错误处理
} }
}; }
/** 用于存储搜索过滤后的流程定义 */ /** 用于存储搜索过滤后的流程定义 */
const filteredProcessDefinitionList = ref< const filteredProcessDefinitionList = ref<
@@ -116,7 +116,7 @@ const filteredProcessDefinitionList = ref<
>([]); >([]);
/** 搜索流程 */ /** 搜索流程 */
const handleQuery = () => { function handleQuery() {
if (searchName.value.trim()) { if (searchName.value.trim()) {
// 如果有搜索关键字,进行过滤 // 如果有搜索关键字,进行过滤
isSearching.value = true; isSearching.value = true;
@@ -141,16 +141,16 @@ const handleQuery = () => {
isSearching.value = false; isSearching.value = false;
filteredProcessDefinitionList.value = processDefinitionList.value; filteredProcessDefinitionList.value = processDefinitionList.value;
} }
}; }
/** 判断流程定义是否匹配搜索 */ /** 判断流程定义是否匹配搜索 */
const isDefinitionMatchSearch = (definition: any) => { function isDefinitionMatchSearch(definition: any) {
if (!isSearching.value) return false; if (!isSearching.value) return false;
return definition.name.toLowerCase().includes(searchName.value.toLowerCase()); return definition.name.toLowerCase().includes(searchName.value.toLowerCase());
}; }
/** 流程定义的分组 */ /** 流程定义的分组 */
const processDefinitionGroup: any = computed(() => { const processDefinitionGroup = computed(() => {
if (!processDefinitionList.value?.length) { if (!processDefinitionList.value?.length) {
return {}; return {};
} }
@@ -172,26 +172,26 @@ const processDefinitionGroup: any = computed(() => {
}); });
/** 通过分类 code 获取对应的名称 */ /** 通过分类 code 获取对应的名称 */
const getCategoryName = (categoryCode: string) => { function getCategoryName(categoryCode: string) {
return categoryList.value?.find((ctg: any) => ctg.code === categoryCode) return categoryList.value?.find((ctg: any) => ctg.code === categoryCode)
?.name; ?.name;
}; }
// ========== 表单相关 ========== // ========== 表单相关 ==========
const selectProcessDefinition = ref(); // 选择的流程定义 const selectProcessDefinition = ref(); // 选择的流程定义
const processDefinitionDetailRef = ref(); const processDefinitionDetailRef = ref();
/** 处理选择流程的按钮操作 */ /** 处理选择流程的按钮操作 */
const handleSelect = async ( async function handleSelect(
row: BpmProcessDefinitionApi.ProcessDefinitionVO, row: BpmProcessDefinitionApi.ProcessDefinitionVO,
formVariables?: any, formVariables?: any,
) => { ) {
// 设置选择的流程 // 设置选择的流程
selectProcessDefinition.value = row; selectProcessDefinition.value = row;
// 初始化流程定义详情 // 初始化流程定义详情
await nextTick(); await nextTick();
processDefinitionDetailRef.value?.initProcessInfo(row, formVariables); processDefinitionDetailRef.value?.initProcessInfo(row, formVariables);
}; }
/** 过滤出有流程的分类列表。目的:只展示有流程的分类 */ /** 过滤出有流程的分类列表。目的:只展示有流程的分类 */
const availableCategories = computed(() => { const availableCategories = computed(() => {
@@ -222,6 +222,7 @@ onMounted(() => {
<template> <template>
<Page auto-content-height> <Page auto-content-height>
<!-- TODO @ziye优先级这里交互可以做成类似 vue3 + element-plus 那个一样滚动切换分类哈对标钉钉飞书哈 -->
<!-- 第一步通过流程定义的列表选择对应的流程 --> <!-- 第一步通过流程定义的列表选择对应的流程 -->
<template v-if="!selectProcessDefinition"> <template v-if="!selectProcessDefinition">
<Card <Card
@@ -274,6 +275,7 @@ onMounted(() => {
}" }"
> >
<div class="flex items-center"> <div class="flex items-center">
<!-- TODO @ziyeiconname 会告警~~ -->
<img <img
v-if="definition.icon" v-if="definition.icon"
:src="definition.icon" :src="definition.icon"

View File

@@ -28,7 +28,8 @@ import {
} from '#/utils'; } from '#/utils';
import ProcessInstanceSimpleViewer from '#/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue'; import ProcessInstanceSimpleViewer from '#/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue';
import ProcessInstanceTimeline from '#/views/bpm/processInstance/detail/modules/time-line.vue'; import ProcessInstanceTimeline from '#/views/bpm/processInstance/detail/modules/time-line.vue';
// 类型定义
/** 类型定义 */
interface ProcessFormData { interface ProcessFormData {
rule: any[]; rule: any[];
option: Record<string, any>; option: Record<string, any>;
@@ -91,7 +92,7 @@ const activityNodes = ref<ApprovalNodeInfo[]>([]);
const processInstanceStartLoading = ref(false); const processInstanceStartLoading = ref(false);
/** 提交按钮 */ /** 提交按钮 */
const submitForm = async () => { async function submitForm() {
if (!fApi.value || !props.selectProcessDefinition) { if (!fApi.value || !props.selectProcessDefinition) {
return; return;
} }
@@ -121,6 +122,7 @@ const submitForm = async () => {
message.success('发起流程成功'); message.success('发起流程成功');
// TODO @ziye有告警哈
closeCurrentTab(); closeCurrentTab();
await router.push({ path: '/bpm/task/my' }); await router.push({ path: '/bpm/task/my' });
@@ -130,10 +132,10 @@ const submitForm = async () => {
} finally { } finally {
processInstanceStartLoading.value = false; processInstanceStartLoading.value = false;
} }
}; }
/** 设置表单信息、获取流程图数据 */ /** 设置表单信息、获取流程图数据 */
const initProcessInfo = async (row: any, formVariables?: any) => { async function initProcessInfo(row: any, formVariables?: any) {
// 重置指定审批人 // 重置指定审批人
startUserSelectTasks.value = []; startUserSelectTasks.value = [];
startUserSelectAssignees.value = {}; startUserSelectAssignees.value = {};
@@ -177,7 +179,7 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
}); });
// 这里暂时无需加载流程图,因为跳出到另外个 Tab // 这里暂时无需加载流程图,因为跳出到另外个 Tab
} }
}; }
/** 预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次 */ /** 预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次 */
watch( watch(
@@ -200,10 +202,10 @@ watch(
); );
/** 获取审批详情 */ /** 获取审批详情 */
const getApprovalDetail = async (row: { async function getApprovalDetail(row: {
id: string; id: string;
processVariablesStr: string; processVariablesStr: string;
}) => { }) {
try { try {
const data = await getApprovalDetailApi({ const data = await getApprovalDetailApi({
processDefinitionId: row.id, processDefinitionId: row.id,
@@ -246,12 +248,12 @@ const getApprovalDetail = async (row: {
message.error('获取审批详情失败'); message.error('获取审批详情失败');
console.error('获取审批详情失败:', error); console.error('获取审批详情失败:', error);
} }
}; }
/** /**
* 设置表单权限 * 设置表单权限
*/ */
const setFieldPermission = (field: string, permission: string) => { function setFieldPermission(field: string, permission: string) {
if (permission === BpmFieldPermissionType.READ) { if (permission === BpmFieldPermissionType.READ) {
// @ts-ignore // @ts-ignore
fApi.value?.disabled(true, field); fApi.value?.disabled(true, field);
@@ -264,18 +266,18 @@ const setFieldPermission = (field: string, permission: string) => {
// @ts-ignore // @ts-ignore
fApi.value?.hidden(true, field); fApi.value?.hidden(true, field);
} }
}; }
/** 取消发起审批 */ /** 取消发起审批 */
const handleCancel = () => { function handleCancel() {
emit('cancel'); emit('cancel');
}; }
/** 选择发起人 */ /** 选择发起人 */
const selectUserConfirm = (activityId: string, userList: any[]) => { function selectUserConfirm(activityId: string, userList: any[]) {
if (!activityId || !Array.isArray(userList)) return; if (!activityId || !Array.isArray(userList)) return;
startUserSelectAssignees.value[activityId] = userList.map((item) => item.id); startUserSelectAssignees.value[activityId] = userList.map((item) => item.id);
}; }
defineExpose({ initProcessInfo }); defineExpose({ initProcessInfo });
</script> </script>
@@ -293,7 +295,7 @@ defineExpose({ initProcessInfo });
<template #extra> <template #extra>
<Space wrap> <Space wrap>
<Button plain type="default" @click="handleCancel"> <Button plain type="default" @click="handleCancel">
<IconifyIcon icon="mdi:arrow-left" />&nbsp; 返回 <IconifyIcon icon="lucide:arrow-left" />&nbsp; 返回
</Button> </Button>
</Space> </Space>
</template> </template>
@@ -345,11 +347,11 @@ defineExpose({ initProcessInfo });
<template v-if="activeTab === 'form'"> <template v-if="activeTab === 'form'">
<Space wrap class="flex w-full justify-center"> <Space wrap class="flex w-full justify-center">
<Button plain type="primary" @click="submitForm"> <Button plain type="primary" @click="submitForm">
<IconifyIcon icon="icon-park-outline:check" /> <IconifyIcon icon="lucide:check" />
发起 发起
</Button> </Button>
<Button plain type="default" @click="handleCancel"> <Button plain type="default" @click="handleCancel">
<IconifyIcon icon="icon-park-outline:close" /> <IconifyIcon icon="lucide:x" />
取消 取消
</Button> </Button>
</Space> </Space>

View File

@@ -1,19 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
import { useAccess } from '@vben/access';
import { getCategorySimpleList } from '#/api/bpm/category'; import { getCategorySimpleList } from '#/api/bpm/category';
import { $t } from '#/locales'; import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
import {
BpmProcessInstanceStatus,
DICT_TYPE,
getDictOptions,
getRangePickerDefaultProps,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
@@ -88,9 +77,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
} }
/** 列表的字段 */ /** 列表的字段 */
export function useGridColumns<T = BpmProcessInstanceApi.ProcessInstanceVO>( export function useGridColumns(): VxeTableGridOptions['columns'] {
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'name', field: 'name',
@@ -136,38 +123,11 @@ export function useGridColumns<T = BpmProcessInstanceApi.ProcessInstanceVO>(
minWidth: 180, minWidth: 180,
formatter: 'formatDateTime', formatter: 'formatDateTime',
}, },
{ {
field: 'operation',
title: '操作', title: '操作',
minWidth: 180, width: 180,
align: 'center',
fixed: 'right', fixed: 'right',
cellRender: { slots: { default: 'actions' },
attrs: {
nameField: 'name',
nameTitle: '流程名称',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: $t('ui.actionTitle.detail'),
show: hasAccessByCodes(['bpm:process-instance:query']),
},
{
code: 'cancel',
text: $t('ui.actionTitle.cancel'),
show: (row: BpmProcessInstanceApi.ProcessInstanceVO) => {
return (
row.status === BpmProcessInstanceStatus.RUNNING &&
hasAccessByCodes(['bpm:process-instance:cancel'])
);
},
},
],
},
}, },
]; ];
} }

View File

@@ -175,7 +175,7 @@ async function getApprovalDetail() {
} }
/** 获取流程模型视图*/ /** 获取流程模型视图*/
const getProcessModelView = async () => { async function getProcessModelView() {
if (BpmModelType.BPMN === processDefinition.value?.modelType) { if (BpmModelType.BPMN === processDefinition.value?.modelType) {
// 重置,解决 BPMN 流程图刷新不会重新渲染问题 // 重置,解决 BPMN 流程图刷新不会重新渲染问题
processModelView.value = { processModelView.value = {
@@ -186,14 +186,14 @@ const getProcessModelView = async () => {
if (data) { if (data) {
processModelView.value = data; processModelView.value = data;
} }
}; }
// 审批节点信息 // 审批节点信息
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]);
/** /**
* 设置表单权限 * 设置表单权限
*/ */
const setFieldPermission = (field: string, permission: string) => { function setFieldPermission(field: string, permission: string) {
if (permission === FieldPermissionType.READ) { if (permission === FieldPermissionType.READ) {
// @ts-ignore // @ts-ignore
fApi.value?.disabled(true, field); fApi.value?.disabled(true, field);
@@ -208,7 +208,7 @@ const setFieldPermission = (field: string, permission: string) => {
// @ts-ignore // @ts-ignore
fApi.value?.hidden(true, field); fApi.value?.hidden(true, field);
} }
}; }
/** /**
* 操作成功后刷新 * 操作成功后刷新
@@ -222,7 +222,7 @@ const setFieldPermission = (field: string, permission: string) => {
const activeTab = ref('form'); const activeTab = ref('form');
const taskListRef = ref(); const taskListRef = ref();
// 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 /** 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 */
watch( watch(
() => activeTab.value, () => activeTab.value,
(newVal) => { (newVal) => {
@@ -238,7 +238,7 @@ watch(
/** 初始化 */ /** 初始化 */
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表 const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
onMounted(async () => { onMounted(async () => {
getDetail(); await getDetail();
// 获得用户列表 // 获得用户列表
userOptions.value = await getSimpleUserList(); userOptions.value = await getSimpleUserList();
}); });

View File

@@ -3,6 +3,7 @@ defineOptions({ name: 'ProcessInstanceBpmnViewer' });
</script> </script>
<template> <template>
<!-- TODO @ziye可以后续找下 antd 有没可以直接用的组件哈 -->
<div> <div>
<h1>BPMN Viewer</h1> <h1>BPMN Viewer</h1>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More