Merge remote-tracking branch 'yudao/master'
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
"build:analyze": "pnpm vite build --mode analyze",
|
||||
"dev": "pnpm vite --mode development",
|
||||
"preview": "vite preview",
|
||||
"#typecheck": "vue-tsc --noEmit --skipLibCheck"
|
||||
"#typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vue-tsc --noEmit --skipLibCheck --incremental --tsBuildInfoFile node_modules/.cache/vue-tsc/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"imports": {
|
||||
"#/*": "./src/*"
|
||||
|
||||
@@ -740,22 +740,18 @@ async function initComponentAdapter() {
|
||||
Rate,
|
||||
RichEditor: withDefaultPlaceholder(VbenTiptap, 'input', {
|
||||
imageUpload: {
|
||||
upload: (file: any, onProgress: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uploadFileApi({
|
||||
file,
|
||||
onProgress({ percent }) {
|
||||
onProgress?.(percent);
|
||||
},
|
||||
onSuccess(response) {
|
||||
// 从响应中提取图片URL
|
||||
resolve(response?.data?.url ?? response?.url ?? '');
|
||||
},
|
||||
onError() {
|
||||
reject(new Error($t('ui.tiptap.upload.uploadFailed')));
|
||||
},
|
||||
upload: async (file: File, onProgress?: (percent: number) => void) => {
|
||||
try {
|
||||
const response = await uploadFileApi({ file }, (progressEvent) => {
|
||||
const percent = progressEvent.total
|
||||
? Math.round((progressEvent.loaded * 100) / progressEvent.total)
|
||||
: 0;
|
||||
onProgress?.(percent);
|
||||
});
|
||||
});
|
||||
return response?.data?.url ?? response?.url ?? '';
|
||||
} catch {
|
||||
throw new Error($t('ui.tiptap.upload.uploadFailed'));
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -14,7 +14,7 @@ export namespace AiChatConversationApi {
|
||||
temperature: number; // 温度参数
|
||||
maxTokens: number; // 单条回复的最大 Token 数量
|
||||
maxContexts: number; // 上下文的最大 Message 数量
|
||||
createTime?: Date; // 创建时间
|
||||
createTime: Date; // 创建时间
|
||||
systemMessage?: string; // 角色设定
|
||||
modelName?: string; // 模型名字
|
||||
roleAvatar?: string; // 角色头像
|
||||
|
||||
@@ -34,15 +34,15 @@ export namespace AiImageApi {
|
||||
prompt: string; // 提示词
|
||||
modelId: number; // 模型
|
||||
style: string; // 图像生成的风格
|
||||
width: string; // 图片宽度
|
||||
height: string; // 图片高度
|
||||
width: number; // 图片宽度
|
||||
height: number; // 图片高度
|
||||
options: object; // 绘制参数,Map<String, String>
|
||||
}
|
||||
|
||||
export interface ImageMidjourneyImagineReqVO {
|
||||
prompt: string; // 提示词
|
||||
modelId: number; // 模型
|
||||
base64Array?: string[]; // size不能为空
|
||||
base64Array: string[]; // 参考图 base64 列表
|
||||
width: string; // 图片宽度
|
||||
height: string; // 图片高度
|
||||
version: string; // 版本
|
||||
|
||||
@@ -12,7 +12,6 @@ export namespace BpmOALeaveApi {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
createTime: Date;
|
||||
startUserSelectAssignees?: Record<string, string[]>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ export namespace BpmProcessInstanceApi {
|
||||
reason: string;
|
||||
signPicUrl: string;
|
||||
status: number;
|
||||
attachments?: string[];
|
||||
}
|
||||
|
||||
/** 抄送流程实例 */
|
||||
|
||||
@@ -11,15 +11,16 @@ export namespace CrmBusinessStatusApi {
|
||||
deptNames?: string[];
|
||||
creator?: string;
|
||||
createTime?: Date;
|
||||
statuses?: BusinessStatusType[];
|
||||
statuses: BusinessStatusType[];
|
||||
}
|
||||
|
||||
/** 商机状态信息 */
|
||||
export interface BusinessStatusType {
|
||||
id?: number;
|
||||
name: string;
|
||||
percent: number;
|
||||
[x: string]: any;
|
||||
percent?: number;
|
||||
endStatus?: number;
|
||||
key?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ export const DEFAULT_STATUSES = [
|
||||
name: '无效',
|
||||
percent: 0,
|
||||
},
|
||||
];
|
||||
] satisfies CrmBusinessStatusApi.BusinessStatusType[];
|
||||
|
||||
/** 查询商机状态组列表 */
|
||||
export function getBusinessStatusPage(params: PageParam) {
|
||||
|
||||
@@ -12,6 +12,7 @@ export namespace CrmReceivableApi {
|
||||
customerId?: number;
|
||||
customerName?: string;
|
||||
contractId?: number;
|
||||
contractNo?: string;
|
||||
contract?: Contract;
|
||||
auditStatus: number;
|
||||
processInstanceId: number;
|
||||
|
||||
@@ -24,6 +24,18 @@ export namespace InfraCodegenApi {
|
||||
parentMenuId: number;
|
||||
}
|
||||
|
||||
/** 代码生成表保存请求 */
|
||||
export interface CodegenTableSaveReqVO extends CodegenTable {
|
||||
frontType?: null | number;
|
||||
genPath?: string;
|
||||
genType?: string;
|
||||
masterTableId?: number;
|
||||
subJoinColumnId?: number;
|
||||
subJoinMany?: boolean;
|
||||
treeParentColumnId?: number;
|
||||
treeNameColumnId?: number;
|
||||
}
|
||||
|
||||
/** 代码生成字段定义 */
|
||||
export interface CodegenColumn {
|
||||
id: number;
|
||||
@@ -54,7 +66,7 @@ export namespace InfraCodegenApi {
|
||||
|
||||
/** 代码生成详情 */
|
||||
export interface CodegenDetail {
|
||||
table: CodegenTable;
|
||||
table: CodegenTableSaveReqVO;
|
||||
columns: CodegenColumn[];
|
||||
}
|
||||
|
||||
@@ -66,7 +78,7 @@ export namespace InfraCodegenApi {
|
||||
|
||||
/** 更新代码生成请求 */
|
||||
export interface CodegenUpdateReqVO {
|
||||
table: any | CodegenTable;
|
||||
table: CodegenTableSaveReqVO;
|
||||
columns: CodegenColumn[];
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export namespace Demo02CategoryApi {
|
||||
}
|
||||
|
||||
/** 查询示例分类列表 */
|
||||
export function getDemo02CategoryList(params: any) {
|
||||
export function getDemo02CategoryList(params?: any) {
|
||||
return requestClient.get<Demo02CategoryApi.Demo02Category[]>(
|
||||
'/infra/demo02-category/list',
|
||||
{ params },
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
@@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
|
||||
export namespace Demo03StudentApi {
|
||||
/** 学生课程信息 */
|
||||
export interface Demo03Course {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
score?: number; // 分数
|
||||
@@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生班级信息 */
|
||||
export interface Demo03Grade {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
teacher?: string; // 班主任
|
||||
@@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生信息 */
|
||||
export interface Demo03Student {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
name?: string; // 名字
|
||||
sex?: number; // 性别
|
||||
birthday?: Dayjs | string; // 出生日期
|
||||
birthday?: number | string; // 出生日期
|
||||
description?: string; // 简介
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
@@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
|
||||
export namespace Demo03StudentApi {
|
||||
/** 学生课程信息 */
|
||||
export interface Demo03Course {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
score?: number; // 分数
|
||||
@@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生班级信息 */
|
||||
export interface Demo03Grade {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
teacher?: string; // 班主任
|
||||
@@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生信息 */
|
||||
export interface Demo03Student {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
name?: string; // 名字
|
||||
sex?: number; // 性别
|
||||
birthday?: Dayjs | string; // 出生日期
|
||||
birthday?: number | string; // 出生日期
|
||||
description?: string; // 简介
|
||||
demo03courses?: Demo03Course[];
|
||||
demo03grade?: Demo03Grade;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
@@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
|
||||
export namespace Demo03StudentApi {
|
||||
/** 学生课程信息 */
|
||||
export interface Demo03Course {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
score?: number; // 分数
|
||||
@@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生班级信息 */
|
||||
export interface Demo03Grade {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
studentId?: number; // 学生编号
|
||||
name?: string; // 名字
|
||||
teacher?: string; // 班主任
|
||||
@@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
|
||||
|
||||
/** 学生信息 */
|
||||
export interface Demo03Student {
|
||||
id: number; // 编号
|
||||
id?: number; // 编号
|
||||
name?: string; // 名字
|
||||
sex?: number; // 性别
|
||||
birthday?: Dayjs | string; // 出生日期
|
||||
birthday?: number | string; // 出生日期
|
||||
description?: string; // 简介
|
||||
demo03courses?: Demo03Course[];
|
||||
demo03grade?: Demo03Grade;
|
||||
|
||||
@@ -6,11 +6,11 @@ export namespace IoTOtaTaskApi {
|
||||
/** IoT OTA 升级任务 */
|
||||
export interface Task {
|
||||
id?: number;
|
||||
name?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
firmwareId?: number;
|
||||
status?: number;
|
||||
deviceScope?: number;
|
||||
deviceScope: number;
|
||||
deviceIds?: number[];
|
||||
deviceTotalCount?: number;
|
||||
deviceSuccessCount?: number;
|
||||
|
||||
@@ -32,8 +32,8 @@ export namespace ThingModelApi {
|
||||
required?: boolean;
|
||||
dataType?: string;
|
||||
description?: string;
|
||||
dataSpecs?: any;
|
||||
dataSpecsList?: any[];
|
||||
dataSpecs?: ThingModelDataSpecs;
|
||||
dataSpecsList?: ThingModelPropertyDataSpecs[];
|
||||
}
|
||||
|
||||
/** IoT 物模型服务 */
|
||||
@@ -66,8 +66,8 @@ export namespace ThingModelApi {
|
||||
direction?: string;
|
||||
paraOrder?: number;
|
||||
dataType?: string;
|
||||
dataSpecs?: any;
|
||||
dataSpecsList?: any[];
|
||||
dataSpecs?: ThingModelDataSpecs;
|
||||
dataSpecsList?: ThingModelPropertyDataSpecs[];
|
||||
}
|
||||
|
||||
/** IoT 物模型 TSL(树形)响应 */
|
||||
@@ -80,19 +80,35 @@ export namespace ThingModelApi {
|
||||
}
|
||||
|
||||
/** IoT 数据定义(数值型) */
|
||||
export interface DataSpecsNumberData {
|
||||
export interface ThingModelDataSpecs {
|
||||
accessMode?: string;
|
||||
childDataType?: string;
|
||||
dataSpecs?: ThingModelDataSpecs;
|
||||
dataSpecsList?: ThingModelPropertyDataSpecs[];
|
||||
dataType?: string;
|
||||
defaultValue?: string;
|
||||
description?: string;
|
||||
identifier?: string;
|
||||
length?: number | string;
|
||||
min?: number | string;
|
||||
max?: number | string;
|
||||
name?: string;
|
||||
precise?: string;
|
||||
required?: boolean;
|
||||
size?: number | string;
|
||||
step?: number | string;
|
||||
unit?: string;
|
||||
unitName?: string;
|
||||
value?: number | string;
|
||||
}
|
||||
|
||||
/** IoT 数据定义(数值型) */
|
||||
export type DataSpecsNumberData = ThingModelDataSpecs;
|
||||
|
||||
/** IoT 数据定义(枚举/布尔型) */
|
||||
export interface DataSpecsEnumOrBoolData {
|
||||
value: number | string;
|
||||
name: string;
|
||||
}
|
||||
export type DataSpecsEnumOrBoolData = ThingModelDataSpecs;
|
||||
|
||||
export type ThingModelPropertyDataSpecs = Property & ThingModelDataSpecs;
|
||||
}
|
||||
|
||||
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
|
||||
|
||||
@@ -76,9 +76,9 @@ export function getSpuPage(params: PageParam) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 tabsCount */
|
||||
export function getTabsCount() {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count');
|
||||
/** 获得商品 SPU 列表 tabsCount(支持按 name/categoryId/createTime 筛选) */
|
||||
export function getTabsCount(params?: Record<string, any>) {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count', { params });
|
||||
}
|
||||
|
||||
/** 创建商品 SPU */
|
||||
|
||||
@@ -8,6 +8,7 @@ export namespace MallCombinationActivityApi {
|
||||
id?: number; // 活动编号
|
||||
name?: string; // 活动名称
|
||||
spuId?: number; // 商品 SPU 编号
|
||||
spuName?: string; // 商品 SPU 名称
|
||||
totalLimitCount?: number; // 总限购数量
|
||||
singleLimitCount?: number; // 单次限购数量
|
||||
startTime?: Date; // 开始时间
|
||||
@@ -21,7 +22,7 @@ export namespace MallCombinationActivityApi {
|
||||
limitDuration?: number; // 限制时长
|
||||
combinationPrice?: number; // 拼团价格
|
||||
products: CombinationProduct[]; // 商品列表
|
||||
picUrl?: any;
|
||||
picUrl?: string; // 商品图片
|
||||
}
|
||||
|
||||
/** 拼团活动所需属性 */
|
||||
|
||||
@@ -26,9 +26,9 @@ export namespace MallRewardActivityApi {
|
||||
conditionType?: number; // 条件类型
|
||||
productScope?: number; // 商品范围
|
||||
rules: RewardRule[]; // 优惠规则列表
|
||||
productScopeValues?: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表
|
||||
productCategoryIds?: number[]; // 商品分类编号列表(仅表单使用)
|
||||
productSpuIds?: number[]; // 商品 SPU 编号列表(仅表单使用)
|
||||
productScopeValues: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表
|
||||
productCategoryIds: number[]; // 商品分类编号列表(仅表单使用)
|
||||
productSpuIds: number[]; // 商品 SPU 编号列表(仅表单使用)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export namespace MallSeckillActivityApi {
|
||||
totalStock?: number; // 秒杀总库存
|
||||
seckillPrice?: number; // 秒杀价格
|
||||
products?: SeckillProduct[]; // 秒杀商品列表
|
||||
picUrl?: any;
|
||||
picUrl?: string; // 商品图片
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,21 @@ export namespace MallTradeConfigApi {
|
||||
/** 交易中心配置 */
|
||||
export interface Config {
|
||||
id?: number;
|
||||
afterSaleRefundReasons?: string[];
|
||||
afterSaleReturnReasons?: string[];
|
||||
deliveryExpressFreeEnabled?: boolean;
|
||||
deliveryExpressFreePrice?: number;
|
||||
deliveryPickUpEnabled?: boolean;
|
||||
afterSaleRefundReasons: string[];
|
||||
afterSaleReturnReasons: string[];
|
||||
deliveryExpressFreeEnabled: boolean;
|
||||
deliveryExpressFreePrice: number;
|
||||
deliveryPickUpEnabled: boolean;
|
||||
brokerageEnabled?: boolean;
|
||||
brokerageEnabledCondition?: number;
|
||||
brokerageBindMode?: number;
|
||||
brokeragePosterUrls?: string;
|
||||
brokeragePosterUrls: string[];
|
||||
brokerageFirstPercent?: number;
|
||||
brokerageSecondPercent?: number;
|
||||
brokerageWithdrawMinPrice?: number;
|
||||
brokerageFrozenDays?: number;
|
||||
brokerageWithdrawTypes?: string;
|
||||
brokerageWithdrawMinPrice: number;
|
||||
brokerageFrozenDays: number;
|
||||
brokerageWithdrawFeePercent: number;
|
||||
brokerageWithdrawTypes: number[];
|
||||
tencentLbsKey?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export namespace MemberAddressApi {
|
||||
name: string;
|
||||
mobile: string;
|
||||
areaId: number;
|
||||
areaName?: string;
|
||||
detailAddress: string;
|
||||
defaultStatus: boolean;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export namespace MemberConfigApi {
|
||||
/** 积分设置信息 */
|
||||
export interface Config {
|
||||
id?: number;
|
||||
pointTradeDeductEnable: number;
|
||||
pointTradeDeductEnable: boolean;
|
||||
pointTradeDeductUnitPrice: number;
|
||||
pointTradeDeductMaxPrice: number;
|
||||
pointTradeGivePoint: number;
|
||||
|
||||
@@ -10,27 +10,31 @@ export namespace MemberUserApi {
|
||||
birthday?: number;
|
||||
createTime?: number;
|
||||
loginDate?: number;
|
||||
loginIp: string;
|
||||
mark: string;
|
||||
mobile: string;
|
||||
loginIp?: string;
|
||||
mark?: string;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
name?: string;
|
||||
nickname?: string;
|
||||
registerIp: string;
|
||||
sex: number;
|
||||
status: number;
|
||||
registerIp?: string;
|
||||
sex?: number;
|
||||
status?: number;
|
||||
areaId?: number;
|
||||
areaName?: string;
|
||||
levelName: string;
|
||||
point?: number;
|
||||
totalPoint?: number;
|
||||
experience?: number;
|
||||
tagIds?: number[];
|
||||
groupId?: number;
|
||||
levelId?: number;
|
||||
levelName?: null | string;
|
||||
point?: null | number;
|
||||
totalPoint?: null | number;
|
||||
experience?: null | number;
|
||||
}
|
||||
|
||||
/** 会员用户等级更新信息 */
|
||||
export interface UserUpdateLevelReqVO {
|
||||
id: number;
|
||||
levelId: number;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/** 会员用户积分更新信息 */
|
||||
|
||||
@@ -17,6 +17,21 @@ export namespace MesWmSnApi {
|
||||
createTime?: Date; // 生成时间
|
||||
}
|
||||
|
||||
/** MES SN 码明细 */
|
||||
export interface Sn {
|
||||
id?: number; // 编号
|
||||
uuid?: string; // 批次 UUID
|
||||
code?: string; // SN 码
|
||||
itemId?: number; // 物料编号
|
||||
itemCode?: string; // 物料编码
|
||||
itemName?: string; // 物料名称
|
||||
specification?: string; // 规格型号
|
||||
unitName?: string; // 单位名称
|
||||
batchCode?: string; // 批次号
|
||||
workOrderId?: number; // 生产工单编号
|
||||
createTime?: Date; // 生成时间
|
||||
}
|
||||
|
||||
/** MES SN 码生成参数 */
|
||||
export interface SnGenerate {
|
||||
itemId?: number; // 物料编号
|
||||
@@ -48,6 +63,13 @@ export function getSnGroupPage(params: MesWmSnApi.PageParams) {
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询批次 SN 码明细列表 */
|
||||
export function getSnListByUuid(uuid: string) {
|
||||
return requestClient.get<MesWmSnApi.Sn[]>('/mes/wm/sn/list-by-uuid', {
|
||||
params: { uuid },
|
||||
});
|
||||
}
|
||||
|
||||
/** 批量删除 SN 码(按批次 UUID) */
|
||||
export function deleteSnBatch(uuid: string) {
|
||||
return requestClient.delete('/mes/wm/sn/delete-batch', {
|
||||
|
||||
@@ -11,6 +11,12 @@ export namespace MpTagApi {
|
||||
count?: number;
|
||||
createTime?: Date;
|
||||
}
|
||||
|
||||
/** 标签精简信息 */
|
||||
export interface SimpleTag {
|
||||
tagId: number;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建公众号标签 */
|
||||
@@ -46,7 +52,7 @@ export function getTagPage(params: PageParam) {
|
||||
|
||||
/** 获取公众号标签精简信息列表 */
|
||||
export function getSimpleTagList() {
|
||||
return requestClient.get<MpTagApi.Tag[]>('/mp/tag/list-all-simple');
|
||||
return requestClient.get<MpTagApi.SimpleTag[]>('/mp/tag/list-all-simple');
|
||||
}
|
||||
|
||||
/** 同步公众号标签 */
|
||||
|
||||
@@ -3,6 +3,16 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace PayNotifyApi {
|
||||
/** 支付通知日志 */
|
||||
export interface NotifyLog {
|
||||
id?: number;
|
||||
status?: number;
|
||||
notifyTimes?: number;
|
||||
lastExecuteTime?: Date;
|
||||
createTime?: Date;
|
||||
response?: string;
|
||||
}
|
||||
|
||||
/** 支付通知任务 */
|
||||
export interface NotifyTask {
|
||||
id: number;
|
||||
@@ -20,13 +30,13 @@ export namespace PayNotifyApi {
|
||||
maxNotifyTimes: number;
|
||||
createTime: Date;
|
||||
updateTime: Date;
|
||||
logs?: any[];
|
||||
logs?: NotifyLog[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得支付通知明细 */
|
||||
export function getNotifyTaskDetail(id: number) {
|
||||
return requestClient.get(`/pay/notify/get-detail?id=${id}`);
|
||||
return requestClient.get<PayNotifyApi.NotifyTask>(`/pay/notify/get-detail?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得支付通知分页 */
|
||||
|
||||
@@ -16,6 +16,7 @@ export namespace SystemUserApi {
|
||||
sex: number;
|
||||
avatar: string;
|
||||
loginIp: string;
|
||||
loginDate?: Date;
|
||||
status: number;
|
||||
remark: string;
|
||||
createTime?: Date;
|
||||
@@ -40,6 +41,13 @@ export function getUser(id: number) {
|
||||
return requestClient.get<SystemUserApi.User>(`/system/user/get?id=${id}`);
|
||||
}
|
||||
|
||||
/** 查询用户列表 */
|
||||
export function getUserList(ids: number[]) {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/list', {
|
||||
params: { ids: ids.join(',') },
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增用户 */
|
||||
export function createUser(data: SystemUserApi.User) {
|
||||
return requestClient.post('/system/user/create', data);
|
||||
@@ -88,13 +96,6 @@ export function updateUserStatus(id: number, status: number) {
|
||||
return requestClient.put('/system/user/update-status', { id, status });
|
||||
}
|
||||
|
||||
/** 查询用户列表 */
|
||||
export function getUserList(ids: number[]) {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/list', {
|
||||
params: { ids: ids.join(',') },
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取用户精简信息列表 */
|
||||
export function getSimpleUserList() {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/simple-list');
|
||||
@@ -111,6 +112,8 @@ export function getSimpleUser(id: number | string) {
|
||||
export function getSimpleUserListByNickname(nickname: string) {
|
||||
return requestClient.get<SystemUserApi.UserSimple[]>(
|
||||
'/system/user/list-by-nickname',
|
||||
{ params: { nickname } },
|
||||
{
|
||||
params: { nickname },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ export function useDescription(options?: Partial<DescriptionProps>) {
|
||||
inheritAttrs: false,
|
||||
setup(_props, { attrs, slots }) {
|
||||
return () => {
|
||||
// @ts-expect-error - 避免类型实例化过深
|
||||
return h(Description, { ...propsState, ...attrs }, slots);
|
||||
};
|
||||
},
|
||||
|
||||
@@ -136,9 +136,13 @@ function autoSearch(queryValue: string) {
|
||||
}
|
||||
|
||||
/** 处理地址选择 */
|
||||
function handleAddressSelect(value: string) {
|
||||
if (value) {
|
||||
regeoCode(value);
|
||||
function handleAddressSelect(value: unknown) {
|
||||
const selectedValue =
|
||||
typeof value === 'object' && value !== null && 'value' in value
|
||||
? (value as { value?: number | string }).value
|
||||
: value;
|
||||
if (selectedValue !== undefined && selectedValue !== null) {
|
||||
regeoCode(String(selectedValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface PopConfirm {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ActionItem extends ButtonProps {
|
||||
export interface ActionItem extends Omit<ButtonProps, 'color'> {
|
||||
onClick?: () => void;
|
||||
type?: ButtonType;
|
||||
label?: string;
|
||||
|
||||
@@ -33,7 +33,7 @@ const getButtonProps = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
async function customRequest(info: UploadRequestOption<any>) {
|
||||
async function customRequest(info: UploadRequestOption) {
|
||||
// 1. emit 上传中
|
||||
const file = info.file as File;
|
||||
const name = file?.name;
|
||||
|
||||
@@ -109,21 +109,6 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
/**
|
||||
* 用于 bpm 移动端流程表单 web-view 的嵌入
|
||||
*/
|
||||
{
|
||||
component: () => import('#/views/bpm/form/mobile/index.vue'),
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
ignoreAccess: true,
|
||||
title: '移动端流程表单展示',
|
||||
},
|
||||
name: 'BpmMobileFormPreview',
|
||||
path: '/bpm/mobile/form-preview',
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
|
||||
@@ -90,7 +90,7 @@ export const useMallKefuStore = defineStore('mall-kefu', {
|
||||
},
|
||||
conversationSort() {
|
||||
// 按置顶属性和最后消息时间排序
|
||||
this.conversationList.toSorted((a, b) => {
|
||||
this.conversationList = this.conversationList.toSorted((a, b) => {
|
||||
// 按照置顶排序,置顶的会在前面
|
||||
if (a.adminPinned !== b.adminPinned) {
|
||||
return a.adminPinned ? -1 : 1;
|
||||
|
||||
@@ -90,7 +90,7 @@ async function getChatConversationList() {
|
||||
// 1.1 获取 对话数据
|
||||
conversationList.value = await getChatConversationMyList();
|
||||
// 1.2 排序
|
||||
conversationList.value.toSorted((a, b) => {
|
||||
conversationList.value = conversationList.value.toSorted((a, b) => {
|
||||
return Number(b.createTime) - Number(a.createTime);
|
||||
});
|
||||
// 1.3 没有任何对话情况
|
||||
|
||||
@@ -22,8 +22,6 @@ const document = ref<null | {
|
||||
}[];
|
||||
title: string;
|
||||
}>(null); // 知识库文档列表
|
||||
const dialogVisible = ref(false); // 知识引用详情弹窗
|
||||
const documentRef = ref<HTMLElement>(); // 知识引用详情弹窗 Ref
|
||||
|
||||
/** 按照 document 聚合 segments */
|
||||
const documentList = computed(() => {
|
||||
@@ -49,7 +47,6 @@ const documentList = computed(() => {
|
||||
/** 点击 document 处理 */
|
||||
function handleClick(doc: any) {
|
||||
document.value = doc;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -79,7 +76,7 @@ function handleClick(doc: any) {
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip placement="topLeft" :trigger="['click']">
|
||||
<div ref="documentRef"></div>
|
||||
<div></div>
|
||||
<template #title>
|
||||
<div class="mb-3 text-base font-bold">{{ document?.title }}</div>
|
||||
<div class="max-h-[60vh] overflow-y-auto">
|
||||
|
||||
@@ -19,11 +19,9 @@ const props = defineProps({
|
||||
});
|
||||
const emits = defineEmits(['onBtnClick', 'onMjBtnClick']);
|
||||
|
||||
const cardImageRef = ref<any>(); // 卡片 image ref
|
||||
|
||||
/** 处理点击事件 */
|
||||
async function handleButtonClick(type: string, detail: AiImageApi.Image) {
|
||||
emits('onBtnClick', type, detail);
|
||||
async function handleButtonClick(type: string) {
|
||||
emits('onBtnClick', type, props.detail);
|
||||
}
|
||||
|
||||
/** 处理 Midjourney 按钮点击事件 */
|
||||
@@ -81,28 +79,28 @@ onMounted(async () => {
|
||||
<Button
|
||||
class="m-0 p-2"
|
||||
type="text"
|
||||
@click="handleButtonClick('download', detail)"
|
||||
@click="handleButtonClick('download')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:download" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-2"
|
||||
type="text"
|
||||
@click="handleButtonClick('regeneration', detail)"
|
||||
@click="handleButtonClick('regeneration')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:refresh-cw" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-2"
|
||||
type="text"
|
||||
@click="handleButtonClick('delete', detail)"
|
||||
@click="handleButtonClick('delete')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-2"
|
||||
type="text"
|
||||
@click="handleButtonClick('more', detail)"
|
||||
@click="handleButtonClick('more')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:ellipsis-vertical" />
|
||||
</Button>
|
||||
@@ -110,7 +108,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
|
||||
<!-- 图片展示区域 -->
|
||||
<div class="mt-5 h-72 flex-1 overflow-hidden" ref="cardImageRef">
|
||||
<div class="mt-5 h-72 flex-1 overflow-hidden">
|
||||
<Image class="w-full rounded-lg" :src="detail?.picUrl" />
|
||||
<div v-if="detail?.status === AiImageStatusEnum.FAIL">
|
||||
{{ detail?.errorMessage }}
|
||||
@@ -121,8 +119,8 @@ onMounted(async () => {
|
||||
<div class="mt-2 flex w-full flex-wrap justify-start">
|
||||
<Button
|
||||
size="small"
|
||||
v-for="(button, index) in detail?.buttons"
|
||||
:key="index"
|
||||
v-for="button in detail?.buttons"
|
||||
:key="button.customId"
|
||||
class="m-2 ml-0 min-w-10"
|
||||
@click="handleMidjourneyBtnClick(button)"
|
||||
>
|
||||
|
||||
@@ -111,8 +111,8 @@ async function handleGenerateImage() {
|
||||
prompt: prompt.value, // 提示词
|
||||
modelId: matchedModel.id, // 使用匹配到的模型
|
||||
style: style.value, // 图像生成的风格
|
||||
width: imageSize.width, // size 不能为空
|
||||
height: imageSize.height, // size 不能为空
|
||||
width: Number(imageSize.width), // size 不能为空
|
||||
height: Number(imageSize.height), // size 不能为空
|
||||
options: {
|
||||
style: style.value, // 图像生成的风格
|
||||
},
|
||||
|
||||
@@ -33,7 +33,6 @@ const queryParams = reactive({
|
||||
}); // 图片分页相关的参数
|
||||
const pageTotal = ref<number>(0); // page size
|
||||
const imageList = ref<AiImageApi.Image[]>([]); // image 列表
|
||||
const imageListRef = ref<any>(); // ref
|
||||
|
||||
const inProgressImageMap = ref<{}>({}); // 监听的 image 映射,一般是生成中(需要轮询),key 为 image 编号,value 为 image
|
||||
const inProgressTimer = ref<any>(); // 生成中的 image 定时器,轮询生成进展
|
||||
@@ -192,7 +191,6 @@ onUnmounted(async () => {
|
||||
|
||||
<div
|
||||
class="flex flex-1 flex-wrap content-start overflow-y-auto p-3 pb-28 pt-5"
|
||||
ref="imageListRef"
|
||||
>
|
||||
<ImageCard
|
||||
v-for="image in imageList"
|
||||
|
||||
@@ -92,6 +92,7 @@ async function handleGenerateImage() {
|
||||
const req = {
|
||||
prompt: prompt.value,
|
||||
modelId: matchedModel.id,
|
||||
base64Array: [],
|
||||
width: imageSize.width,
|
||||
height: imageSize.height,
|
||||
version: selectVersion.value,
|
||||
|
||||
@@ -44,8 +44,8 @@ const currentFile = ref<any>(null); // 当前选中的文件
|
||||
const submitLoading = ref(false); // 提交按钮加载状态
|
||||
|
||||
/** 选择文件 */
|
||||
async function selectFile(index: number) {
|
||||
currentFile.value = modelData.value.list[index];
|
||||
async function selectFile(index: number | string) {
|
||||
currentFile.value = modelData.value.list[Number(index)];
|
||||
await splitContentFile(currentFile.value);
|
||||
}
|
||||
|
||||
@@ -258,7 +258,8 @@ onMounted(async () => {
|
||||
class="mb-2.5"
|
||||
>
|
||||
<div class="mb-1 text-sm text-gray-500">
|
||||
分片-{{ index + 1 }} · {{ segment.contentLength || 0 }} 字符数 ·
|
||||
分片-{{ Number(index) + 1 }} ·
|
||||
{{ segment.contentLength || 0 }} 字符数 ·
|
||||
{{ segment.tokens || 0 }} Token
|
||||
</div>
|
||||
<div class="rounded-md bg-card p-2">
|
||||
|
||||
@@ -22,8 +22,6 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const formRef = ref(); // 表单引用
|
||||
const uploadRef = ref(); // 上传组件引用
|
||||
const parent = inject('parent', null); // 获取父组件实例
|
||||
const { uploadUrl, httpRequest } = useUpload(); // 使用上传组件的钩子
|
||||
const fileList = ref<UploadProps['fileList']>([]); // 文件列表
|
||||
@@ -147,10 +145,10 @@ async function customRequest(info: UploadRequestOption) {
|
||||
*
|
||||
* @param index 要移除的文件索引
|
||||
*/
|
||||
function removeFile(index: number) {
|
||||
function removeFile(index: number | string) {
|
||||
// 从列表中移除文件
|
||||
const newList = [...props.modelValue.list];
|
||||
newList.splice(index, 1);
|
||||
newList.splice(Number(index), 1);
|
||||
// 更新表单数据
|
||||
emit('update:modelValue', {
|
||||
...props.modelValue,
|
||||
@@ -185,14 +183,13 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form ref="formRef" :model="modelData" label-width="0" class="mt-5">
|
||||
<Form :model="modelData" label-width="0" class="mt-5">
|
||||
<FormItem class="mb-5">
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="w-full rounded-md border-2 border-dashed border-gray-200 p-5 text-center hover:border-blue-500"
|
||||
>
|
||||
<UploadDragger
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
:action="uploadUrl"
|
||||
v-model:file-list="fileList"
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject, reactive, ref } from 'vue';
|
||||
import type { MusicSong } from '../types';
|
||||
|
||||
import { computed, inject, nextTick, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatPast } from '@vben/utils';
|
||||
|
||||
import { Image, Slider } from 'antdv-next';
|
||||
|
||||
import { currentSongKey } from '../types';
|
||||
|
||||
defineOptions({ name: 'AiMusicAudioBarIndex' });
|
||||
|
||||
const currentSong = inject<any>('currentSong', {});
|
||||
const currentSong = inject(currentSongKey, ref<MusicSong>({}));
|
||||
const currentAudioUrl = computed(() => currentSong.value.audioUrl || undefined);
|
||||
|
||||
const audioRef = ref<HTMLAudioElement | null>(null);
|
||||
const audioProgress = ref(0);
|
||||
const audioDuration = ref(0);
|
||||
const audioProps = reactive<any>({
|
||||
autoplay: true,
|
||||
paused: false,
|
||||
@@ -20,6 +26,17 @@ const audioProps = reactive<any>({
|
||||
volume: 50,
|
||||
}); // 音频相关属性https://www.runoob.com/tags/ref-av-dom.html
|
||||
|
||||
function formatAudioTime(seconds: number) {
|
||||
if (!Number.isFinite(seconds)) {
|
||||
return '00:00';
|
||||
}
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = Math.floor(seconds % 60);
|
||||
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds
|
||||
.toString()
|
||||
.padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function toggleStatus(type: string) {
|
||||
audioProps[type] = !audioProps[type];
|
||||
if (type === 'paused' && audioRef.value) {
|
||||
@@ -32,9 +49,40 @@ function toggleStatus(type: string) {
|
||||
}
|
||||
|
||||
/** 更新播放位置 */
|
||||
function audioTimeUpdate(args: any) {
|
||||
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss');
|
||||
function audioTimeUpdate() {
|
||||
if (!audioRef.value) {
|
||||
return;
|
||||
}
|
||||
audioProgress.value = audioRef.value.currentTime;
|
||||
audioProps.currentTime = formatAudioTime(audioRef.value.currentTime);
|
||||
}
|
||||
|
||||
function audioLoadedMetadata() {
|
||||
if (!audioRef.value) {
|
||||
return;
|
||||
}
|
||||
audioDuration.value = audioRef.value.duration;
|
||||
audioProps.duration = formatAudioTime(audioRef.value.duration);
|
||||
}
|
||||
|
||||
function handleProgressChange(value: number | [number, number]) {
|
||||
if (!audioRef.value || Array.isArray(value)) {
|
||||
return;
|
||||
}
|
||||
audioRef.value.currentTime = value;
|
||||
audioProgress.value = value;
|
||||
audioProps.currentTime = formatAudioTime(value);
|
||||
}
|
||||
|
||||
watch(currentAudioUrl, () => {
|
||||
audioProgress.value = 0;
|
||||
audioDuration.value = 0;
|
||||
audioProps.currentTime = '00:00';
|
||||
audioProps.duration = '00:00';
|
||||
nextTick(() => {
|
||||
audioRef.value?.load();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -48,8 +96,10 @@ function audioTimeUpdate(args: any) {
|
||||
:width="45"
|
||||
/>
|
||||
<div>
|
||||
<div>{{ currentSong.name }}</div>
|
||||
<div class="text-xs text-gray-400">{{ currentSong.singer }}</div>
|
||||
<div>{{ currentSong.title || '暂无音乐' }}</div>
|
||||
<div class="text-xs text-gray-400">
|
||||
{{ currentSong.singer || currentSong.desc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 音频controls -->
|
||||
@@ -74,22 +124,25 @@ function audioTimeUpdate(args: any) {
|
||||
<div class="flex items-center gap-4">
|
||||
<span>{{ audioProps.currentTime }}</span>
|
||||
<Slider
|
||||
v-model:value="audioProps.duration"
|
||||
v-model:value="audioProgress"
|
||||
:max="audioDuration"
|
||||
color="#409eff"
|
||||
class="!w-40"
|
||||
@change="handleProgressChange"
|
||||
/>
|
||||
<span>{{ audioProps.duration }}</span>
|
||||
</div>
|
||||
<!-- 音频 -->
|
||||
<audio
|
||||
v-bind="audioProps"
|
||||
:src="currentAudioUrl"
|
||||
:autoplay="audioProps.autoplay"
|
||||
:muted="audioProps.muted"
|
||||
ref="audioRef"
|
||||
controls
|
||||
v-show="!audioProps"
|
||||
@timeupdate="audioTimeUpdate"
|
||||
>
|
||||
<!-- <source :src="audioUrl" /> -->
|
||||
</audio>
|
||||
@loadedmetadata="audioLoadedMetadata"
|
||||
></audio>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<IconifyIcon
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { MusicSong } from './types';
|
||||
|
||||
import { provide, ref } from 'vue';
|
||||
|
||||
@@ -8,14 +9,15 @@ import { Col, Empty, Row, TabPane, Tabs } from 'antdv-next';
|
||||
import audioBar from './audioBar/index.vue';
|
||||
import songCard from './songCard/index.vue';
|
||||
import songInfo from './songInfo/index.vue';
|
||||
import { currentSongKey } from './types';
|
||||
|
||||
defineOptions({ name: 'AiMusicListIndex' });
|
||||
|
||||
const currentType = ref('mine');
|
||||
const loading = ref(false); // loading 状态
|
||||
const currentSong = ref({}); // 当前音乐
|
||||
const mySongList = ref<Recordable<any>[]>([]);
|
||||
const squareSongList = ref<Recordable<any>[]>([]);
|
||||
const currentSong = ref<MusicSong>({}); // 当前音乐
|
||||
const mySongList = ref<MusicSong[]>([]);
|
||||
const squareSongList = ref<MusicSong[]>([]);
|
||||
|
||||
function generateMusic(_formData: Recordable<any>) {
|
||||
loading.value = true;
|
||||
@@ -45,7 +47,7 @@ function generateMusic(_formData: Recordable<any>) {
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function setCurrentSong(music: Recordable<any>) {
|
||||
function setCurrentSong(music: MusicSong) {
|
||||
currentSong.value = music;
|
||||
}
|
||||
|
||||
@@ -53,7 +55,7 @@ defineExpose({
|
||||
generateMusic,
|
||||
});
|
||||
|
||||
provide('currentSong', currentSong);
|
||||
provide(currentSongKey, currentSong);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue';
|
||||
import type { MusicSong } from '../types';
|
||||
|
||||
import { inject, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Image } from 'antdv-next';
|
||||
|
||||
import { currentSongKey } from '../types';
|
||||
|
||||
defineOptions({ name: 'AiMusicSongCardIndex' });
|
||||
|
||||
defineProps({
|
||||
songInfo: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
withDefaults(defineProps<{ songInfo?: MusicSong }>(), {
|
||||
songInfo: () => ({}),
|
||||
});
|
||||
|
||||
const emits = defineEmits(['play']);
|
||||
|
||||
const currentSong = inject<any>('currentSong', {});
|
||||
const currentSong = inject(currentSongKey, ref<MusicSong>({}));
|
||||
|
||||
function playSong() {
|
||||
emits('play');
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue';
|
||||
import type { MusicSong } from '../types';
|
||||
|
||||
import { inject, ref } from 'vue';
|
||||
|
||||
import { Button, Card, Image } from 'antdv-next';
|
||||
|
||||
import { currentSongKey } from '../types';
|
||||
|
||||
defineOptions({ name: 'AiMusicSongInfoIndex' });
|
||||
|
||||
const currentSong = inject<any>('currentSong', {});
|
||||
const currentSong = inject(currentSongKey, ref<MusicSong>({}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
16
apps/web-antdv-next/src/views/ai/music/index/list/types.ts
Normal file
16
apps/web-antdv-next/src/views/ai/music/index/list/types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
|
||||
export interface MusicSong {
|
||||
audioUrl?: string;
|
||||
date?: string;
|
||||
desc?: string;
|
||||
id?: number;
|
||||
imageUrl?: string;
|
||||
lyric?: string;
|
||||
singer?: string;
|
||||
title?: string;
|
||||
videoUrl?: string;
|
||||
}
|
||||
|
||||
export const currentSongKey: InjectionKey<Ref<MusicSong>> =
|
||||
Symbol('currentSong');
|
||||
@@ -245,6 +245,9 @@ const moddleExtensions = computed(() => {
|
||||
const initBpmnModeler = () => {
|
||||
if (bpmnModeler) return;
|
||||
const data: any = document.querySelector('#bpmnCanvas');
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
// console.log(data, 'data');
|
||||
// console.log(props.keyboard, 'props.keyboard');
|
||||
// console.log(additionalModules, 'additionalModules()');
|
||||
@@ -261,7 +264,7 @@ const initBpmnModeler = () => {
|
||||
// propertiesPanel: {
|
||||
// parent: '#js-properties-panel'
|
||||
// },
|
||||
keyboard: props.keyboard ? { bindTo: document } : null,
|
||||
keyboard: props.keyboard ? { bind: true } : null,
|
||||
// additionalModules: additionalModules.value,
|
||||
additionalModules: additionalModules.value as any[],
|
||||
moddleExtensions: moddleExtensions.value,
|
||||
|
||||
@@ -48,6 +48,25 @@ const dialogVisible = ref(false); // 弹窗可见性
|
||||
const dialogTitle = ref<string | undefined>(undefined); // 弹窗标题
|
||||
const selectActivityType = ref<string | undefined>(undefined); // 选中 Task 的活动编号
|
||||
const selectTasks = ref<any[]>([]); // 选中的任务数组
|
||||
type BpmnCanvas = {
|
||||
_svg?: SVGSVGElement;
|
||||
addMarker: (element: any, marker: string) => void;
|
||||
removeMarker: (element: any, marker: string) => void;
|
||||
zoom: (
|
||||
newScale?: 'fit-viewport' | number,
|
||||
center?: 'auto' | { x: number; y: number },
|
||||
) => number;
|
||||
};
|
||||
type ElementRegistry = {
|
||||
filter: (callback: (element: any) => boolean) => any[];
|
||||
get: (id: string) => any;
|
||||
};
|
||||
|
||||
const getCanvas = () =>
|
||||
bpmnViewer.value?.get('canvas') as BpmnCanvas | undefined;
|
||||
const getElementRegistry = () =>
|
||||
bpmnViewer.value?.get('elementRegistry') as ElementRegistry | undefined;
|
||||
|
||||
const approvalColumns = computed<TableColumnType[]>(() => {
|
||||
const userColumn: TableColumnType =
|
||||
selectActivityType.value === 'bpmn:UserTask'
|
||||
@@ -126,7 +145,7 @@ const approvalColumns = computed<TableColumnType[]>(() => {
|
||||
/** Zoom:恢复 */
|
||||
const processReZoom = () => {
|
||||
defaultZoom.value = 1;
|
||||
bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto');
|
||||
getCanvas()?.zoom('fit-viewport', 'auto');
|
||||
};
|
||||
|
||||
let resizeObserver: null | ResizeObserver = null;
|
||||
@@ -173,7 +192,7 @@ const processZoomIn = (zoomStep = 0.1) => {
|
||||
);
|
||||
}
|
||||
defaultZoom.value = newZoom;
|
||||
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value);
|
||||
getCanvas()?.zoom(defaultZoom.value);
|
||||
};
|
||||
|
||||
/** Zoom:缩小 */
|
||||
@@ -185,7 +204,7 @@ const processZoomOut = (zoomStep = 0.1) => {
|
||||
);
|
||||
}
|
||||
defaultZoom.value = newZoom;
|
||||
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value);
|
||||
getCanvas()?.zoom(defaultZoom.value);
|
||||
};
|
||||
|
||||
/** 流程图预览清空 */
|
||||
@@ -206,9 +225,9 @@ const addCustomDefs = () => {
|
||||
if (!bpmnViewer.value) {
|
||||
return;
|
||||
}
|
||||
const canvas = bpmnViewer.value?.get('canvas');
|
||||
const canvas = getCanvas();
|
||||
const svg = canvas?._svg;
|
||||
svg.append(customDefs.value);
|
||||
svg?.append(customDefs.value);
|
||||
};
|
||||
|
||||
/** 节点选中 */
|
||||
@@ -304,8 +323,11 @@ const setProcessStatus = (view: any) => {
|
||||
finishedSequenceFlowActivityIds,
|
||||
rejectedTaskActivityIds,
|
||||
} = view;
|
||||
const canvas: any = bpmnViewer.value.get('canvas');
|
||||
const elementRegistry: any = bpmnViewer.value.get('elementRegistry');
|
||||
const canvas = getCanvas();
|
||||
const elementRegistry = getElementRegistry();
|
||||
if (!canvas || !elementRegistry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 已完成节点
|
||||
if (Array.isArray(finishedSequenceFlowActivityIds)) {
|
||||
@@ -313,7 +335,7 @@ const setProcessStatus = (view: any) => {
|
||||
if (item !== null) {
|
||||
canvas.addMarker(item, 'success');
|
||||
const element = elementRegistry.get(item);
|
||||
const conditionExpression = element.businessObject.conditionExpression;
|
||||
const conditionExpression = element?.businessObject.conditionExpression;
|
||||
if (conditionExpression) {
|
||||
canvas.addMarker(item, 'condition-expression');
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Divider orientation="left">审批人超时未处理时</Divider>
|
||||
<Divider title-placement="left">审批人超时未处理时</Divider>
|
||||
<FormItem label="启用开关" name="timeoutHandlerEnable">
|
||||
<Switch
|
||||
v-model:checked="timeoutHandlerEnable"
|
||||
|
||||
@@ -447,7 +447,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Divider orientation="left">审批类型</Divider>
|
||||
<Divider title-placement="left">审批类型</Divider>
|
||||
<FormItem name="approveType" label="审批类型">
|
||||
<RadioGroup v-model:value="approveType.value">
|
||||
<Radio
|
||||
@@ -460,7 +460,7 @@ onMounted(async () => {
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批人拒绝时</Divider>
|
||||
<Divider title-placement="left">审批人拒绝时</Divider>
|
||||
<FormItem name="rejectHandlerType" label="处理方式">
|
||||
<RadioGroup
|
||||
v-model:value="rejectHandlerType"
|
||||
@@ -492,7 +492,7 @@ onMounted(async () => {
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批人为空时</Divider>
|
||||
<Divider title-placement="left">审批人为空时</Divider>
|
||||
<FormItem name="assignEmptyHandlerType">
|
||||
<RadioGroup
|
||||
v-model:value="assignEmptyHandlerType"
|
||||
@@ -523,7 +523,7 @@ onMounted(async () => {
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批人与提交人为同一人时</Divider>
|
||||
<Divider title-placement="left">审批人与提交人为同一人时</Divider>
|
||||
<RadioGroup
|
||||
v-model:value="assignStartUserHandlerType"
|
||||
@change="updateAssignStartUserHandlerType"
|
||||
@@ -540,7 +540,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
<Divider orientation="left">操作按钮</Divider>
|
||||
<Divider title-placement="left">操作按钮</Divider>
|
||||
<div class="mt-2 text-sm">
|
||||
<!-- 头部标题行 -->
|
||||
<div
|
||||
@@ -587,7 +587,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider orientation="left">字段权限</Divider>
|
||||
<Divider title-placement="left">字段权限</Divider>
|
||||
<div v-if="formType === BpmModelFormType.NORMAL" class="mt-2 text-sm">
|
||||
<!-- 头部标题行 -->
|
||||
<div
|
||||
@@ -663,7 +663,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider orientation="left">是否需要签名</Divider>
|
||||
<Divider title-placement="left">是否需要签名</Divider>
|
||||
<FormItem name="signEnable">
|
||||
<Switch
|
||||
v-model:checked="signEnable.value"
|
||||
@@ -673,7 +673,7 @@ onMounted(async () => {
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批意见</Divider>
|
||||
<Divider title-placement="left">审批意见</Divider>
|
||||
<FormItem name="reasonRequire">
|
||||
<Switch
|
||||
v-model:checked="reasonRequire.value"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!-- eslint-disable no-unused-vars -->
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, nextTick, onMounted, ref, toRaw, watch } from 'vue';
|
||||
|
||||
@@ -24,26 +23,7 @@ const prefix = inject('prefix');
|
||||
|
||||
const formKey = ref<number | string | undefined>(undefined);
|
||||
const businessKey = ref('');
|
||||
const optionModelTitle = ref('');
|
||||
const fieldList = ref<any[]>([]);
|
||||
const formFieldForm = ref<any>({});
|
||||
const fieldType = ref({
|
||||
long: '长整型',
|
||||
string: '字符串',
|
||||
boolean: '布尔类',
|
||||
date: '日期类',
|
||||
enum: '枚举类',
|
||||
custom: '自定义类型',
|
||||
});
|
||||
const formFieldIndex = ref(-1); // 编辑中的字段, -1 为新增
|
||||
const formFieldOptionIndex = ref(-1); // 编辑中的字段配置项, -1 为新增
|
||||
const fieldModelVisible = ref(false);
|
||||
const fieldOptionModelVisible = ref(false);
|
||||
const fieldOptionForm = ref<any>({}); // 当前激活的字段配置项数据
|
||||
const fieldOptionType = ref(''); // 当前激活的字段配置项弹窗 类型
|
||||
const fieldEnumList = ref<any[]>([]); // 枚举值列表
|
||||
const fieldConstraintsList = ref<any[]>([]); // 约束条件列表
|
||||
const fieldPropertiesList = ref<any[]>([]); // 绑定属性列表
|
||||
const bpmnELement = ref();
|
||||
const elExtensionElements = ref();
|
||||
const formData = ref();
|
||||
@@ -94,173 +74,6 @@ const _updateElementBusinessKey = () => {
|
||||
},
|
||||
);
|
||||
};
|
||||
// 根据类型调整字段type
|
||||
const _changeFieldTypeType = (type: any) => {
|
||||
formFieldForm.value.type = type === 'custom' ? '' : type;
|
||||
};
|
||||
|
||||
// 打开字段详情侧边栏
|
||||
const _openFieldForm = (field: any, index: any) => {
|
||||
formFieldIndex.value = index;
|
||||
if (index === -1) {
|
||||
formFieldForm.value = {};
|
||||
// 初始化枚举值列表
|
||||
fieldEnumList.value = [];
|
||||
// 初始化约束条件列表
|
||||
fieldConstraintsList.value = [];
|
||||
// 初始化自定义属性列表
|
||||
fieldPropertiesList.value = [];
|
||||
} else {
|
||||
const FieldObject = formData.value.fields[index];
|
||||
formFieldForm.value = cloneDeep(field);
|
||||
// 设置自定义类型
|
||||
// this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
|
||||
formFieldForm.value.typeType = fieldType.value[
|
||||
field.type as keyof typeof fieldType.value
|
||||
]
|
||||
? field.type
|
||||
: 'custom';
|
||||
// 初始化枚举值列表
|
||||
field.type === 'enum' &&
|
||||
(fieldEnumList.value = cloneDeep(FieldObject?.values || []));
|
||||
// 初始化约束条件列表
|
||||
fieldConstraintsList.value = cloneDeep(
|
||||
FieldObject?.validation?.constraints || [],
|
||||
);
|
||||
// 初始化自定义属性列表
|
||||
fieldPropertiesList.value = cloneDeep(
|
||||
FieldObject?.properties?.values || [],
|
||||
);
|
||||
}
|
||||
fieldModelVisible.value = true;
|
||||
};
|
||||
// 打开字段 某个 配置项 弹窗
|
||||
const _openFieldOptionForm = (option: any, index: any, type: any) => {
|
||||
fieldOptionModelVisible.value = true;
|
||||
fieldOptionType.value = type;
|
||||
formFieldOptionIndex.value = index;
|
||||
if (type === 'property') {
|
||||
fieldOptionForm.value = option ? cloneDeep(option) : {};
|
||||
return (optionModelTitle.value = '属性配置');
|
||||
}
|
||||
if (type === 'enum') {
|
||||
fieldOptionForm.value = option ? cloneDeep(option) : {};
|
||||
return (optionModelTitle.value = '枚举值配置');
|
||||
}
|
||||
fieldOptionForm.value = option ? cloneDeep(option) : {};
|
||||
return (optionModelTitle.value = '约束条件配置');
|
||||
};
|
||||
|
||||
// 保存字段 某个 配置项
|
||||
const _saveFieldOption = () => {
|
||||
if (formFieldOptionIndex.value === -1) {
|
||||
if (fieldOptionType.value === 'property') {
|
||||
fieldPropertiesList.value.push(fieldOptionForm.value);
|
||||
}
|
||||
if (fieldOptionType.value === 'constraint') {
|
||||
fieldConstraintsList.value.push(fieldOptionForm.value);
|
||||
}
|
||||
if (fieldOptionType.value === 'enum') {
|
||||
fieldEnumList.value.push(fieldOptionForm.value);
|
||||
}
|
||||
} else {
|
||||
fieldOptionType.value === 'property' &&
|
||||
fieldPropertiesList.value.splice(
|
||||
formFieldOptionIndex.value,
|
||||
1,
|
||||
fieldOptionForm.value,
|
||||
);
|
||||
fieldOptionType.value === 'constraint' &&
|
||||
fieldConstraintsList.value.splice(
|
||||
formFieldOptionIndex.value,
|
||||
1,
|
||||
fieldOptionForm.value,
|
||||
);
|
||||
fieldOptionType.value === 'enum' &&
|
||||
fieldEnumList.value.splice(
|
||||
formFieldOptionIndex.value,
|
||||
1,
|
||||
fieldOptionForm.value,
|
||||
);
|
||||
}
|
||||
fieldOptionModelVisible.value = false;
|
||||
fieldOptionForm.value = {};
|
||||
};
|
||||
// 保存字段配置
|
||||
const _saveField = () => {
|
||||
const { id, type, label, defaultValue, datePattern } = formFieldForm.value;
|
||||
const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, {
|
||||
id,
|
||||
type,
|
||||
label,
|
||||
});
|
||||
defaultValue && (Field.defaultValue = defaultValue);
|
||||
datePattern && (Field.datePattern = datePattern);
|
||||
// 构建属性
|
||||
if (fieldPropertiesList.value && fieldPropertiesList.value.length > 0) {
|
||||
const fieldPropertyList = fieldPropertiesList.value.map((fp: any) => {
|
||||
return bpmnInstances().moddle.create(`${prefix}:Property`, {
|
||||
id: fp.id,
|
||||
value: fp.value,
|
||||
});
|
||||
});
|
||||
Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
||||
values: fieldPropertyList,
|
||||
});
|
||||
}
|
||||
// 构建校验规则
|
||||
if (fieldConstraintsList.value && fieldConstraintsList.value.length > 0) {
|
||||
const fieldConstraintList = fieldConstraintsList.value.map((fc: any) => {
|
||||
return bpmnInstances().moddle.create(`${prefix}:Constraint`, {
|
||||
name: fc.name,
|
||||
config: fc.config,
|
||||
});
|
||||
});
|
||||
Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, {
|
||||
constraints: fieldConstraintList,
|
||||
});
|
||||
}
|
||||
// 构建枚举值
|
||||
if (fieldEnumList.value && fieldEnumList.value.length > 0) {
|
||||
Field.values = fieldEnumList.value.map((fe: any) => {
|
||||
return bpmnInstances().moddle.create(`${prefix}:Value`, {
|
||||
name: fe.name,
|
||||
id: fe.id,
|
||||
});
|
||||
});
|
||||
}
|
||||
// 更新数组 与 表单配置实例
|
||||
if (formFieldIndex.value === -1) {
|
||||
fieldList.value.push(formFieldForm.value);
|
||||
formData.value.fields.push(Field);
|
||||
} else {
|
||||
fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value);
|
||||
formData.value.fields.splice(formFieldIndex.value, 1, Field);
|
||||
}
|
||||
updateElementExtensions();
|
||||
fieldModelVisible.value = false;
|
||||
};
|
||||
|
||||
// 移除某个 字段的 配置项
|
||||
const _removeFieldOptionItem = (_option: any, index: any, type: any) => {
|
||||
// console.log(option, 'option')
|
||||
if (type === 'property') {
|
||||
fieldPropertiesList.value.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
if (type === 'enum') {
|
||||
fieldEnumList.value.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
fieldConstraintsList.value.splice(index, 1);
|
||||
};
|
||||
// 移除 字段
|
||||
const _removeField = (field: any, index: any) => {
|
||||
console.warn(field, 'field');
|
||||
fieldList.value.splice(index, 1);
|
||||
formData.value.fields.splice(index, 1);
|
||||
updateElementExtensions();
|
||||
};
|
||||
|
||||
const updateElementExtensions = () => {
|
||||
// 更新回扩展元素
|
||||
@@ -328,210 +141,5 @@ watch(
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
<!--字段列表-->
|
||||
<!-- <div class="element-property list-property">-->
|
||||
<!-- <Divider><Icon icon="ep:coin" /> 表单字段</Divider>-->
|
||||
<!-- <Table :data-source="fieldList" :scroll="{ y: 240 }" bordered>-->
|
||||
<!-- <TableColumn title="序号" type="index" width="50px" />-->
|
||||
<!-- <TableColumn title="字段名称" dataIndex="label" width="80px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn-->
|
||||
<!-- title="字段类型"-->
|
||||
<!-- dataIndex="type"-->
|
||||
<!-- width="80px"-->
|
||||
<!-- :customRender="({ text }) => fieldType[text] || text"-->
|
||||
<!-- :ellipsis="true"-->
|
||||
<!-- />-->
|
||||
<!-- <TableColumn-->
|
||||
<!-- title="默认值"-->
|
||||
<!-- dataIndex="defaultValue"-->
|
||||
<!-- width="80px"-->
|
||||
<!-- :ellipsis="true"-->
|
||||
<!-- />-->
|
||||
<!-- <TableColumn title="操作" width="90px">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <Button type="link" @click="openFieldForm(scope, scope.$index)">-->
|
||||
<!-- 编辑-->
|
||||
<!-- </Button>-->
|
||||
<!-- <Divider type="vertical" />-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- danger-->
|
||||
<!-- @click="removeField(scope, scope.$index)"-->
|
||||
<!-- >-->
|
||||
<!-- 移除-->
|
||||
<!-- </Button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </TableColumn>-->
|
||||
<!-- </Table>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="element-drawer__button">-->
|
||||
<!-- <Button type="primary" @click="openFieldForm(null, -1)">添加字段</Button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!--字段配置侧边栏-->
|
||||
<!-- <Drawer-->
|
||||
<!-- v-model:open="fieldModelVisible"-->
|
||||
<!-- title="字段配置"-->
|
||||
<!-- :width="`${width}px`"-->
|
||||
<!-- destroyOnClose-->
|
||||
<!-- >-->
|
||||
<!-- <Form :model="formFieldForm" :label-col="{ style: { width: '90px' } }">-->
|
||||
<!-- <FormItem label="字段ID">-->
|
||||
<!-- <Input v-model:value="formFieldForm.id" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="类型">-->
|
||||
<!-- <Select-->
|
||||
<!-- v-model:value="formFieldForm.typeType"-->
|
||||
<!-- placeholder="请选择字段类型"-->
|
||||
<!-- allowClear-->
|
||||
<!-- @change="changeFieldTypeType"-->
|
||||
<!-- >-->
|
||||
<!-- </Select>-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
|
||||
<!-- <Input v-model:value="formFieldForm.type" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="名称">-->
|
||||
<!-- <Input v-model:value="formFieldForm.label" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
|
||||
<!-- <Input v-model:value="formFieldForm.datePattern" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="默认值">-->
|
||||
<!-- <Input v-model:value="formFieldForm.defaultValue" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- </Form>-->
|
||||
|
||||
<!-- <!– 枚举值设置 –>-->
|
||||
<!-- <template v-if="formFieldForm.type === 'enum'">-->
|
||||
<!-- <Divider key="enum-divider" />-->
|
||||
<!-- <p class="listener-filed__title" key="enum-title">-->
|
||||
<!-- <span><Icon icon="ep:menu" />枚举值列表:</span>-->
|
||||
<!-- <Button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
|
||||
<!-- >添加枚举值</Button-->
|
||||
<!-- >-->
|
||||
<!-- </p>-->
|
||||
<!-- <Table :data-source="fieldEnumList" key="enum-table" :scroll="{ y: 240 }" bordered>-->
|
||||
<!-- <TableColumn title="序号" width="50px" type="index" />-->
|
||||
<!-- <TableColumn title="枚举值编号" dataIndex="id" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="枚举值名称" dataIndex="name" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="操作" width="90px">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
|
||||
<!-- >-->
|
||||
<!-- 编辑-->
|
||||
<!-- </Button>-->
|
||||
<!-- <Divider type="vertical" />-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- danger-->
|
||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
|
||||
<!-- >-->
|
||||
<!-- 移除-->
|
||||
<!-- </Button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </TableColumn>-->
|
||||
<!-- </Table>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- <!– 校验规则 –>-->
|
||||
<!-- <Divider key="validation-divider" />-->
|
||||
<!-- <p class="listener-filed__title" key="validation-title">-->
|
||||
<!-- <span><Icon icon="ep:menu" />约束条件列表:</span>-->
|
||||
<!-- <Button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
|
||||
<!-- >添加约束</Button-->
|
||||
<!-- >-->
|
||||
<!-- </p>-->
|
||||
<!-- <Table :data-source="fieldConstraintsList" key="validation-table" :scroll="{ y: 240 }" bordered>-->
|
||||
<!-- <TableColumn title="序号" width="50px" type="index" />-->
|
||||
<!-- <TableColumn title="约束名称" dataIndex="name" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="约束配置" dataIndex="config" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="操作" width="90px">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
|
||||
<!-- >-->
|
||||
<!-- 编辑-->
|
||||
<!-- </Button>-->
|
||||
<!-- <Divider type="vertical" />-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- danger-->
|
||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
|
||||
<!-- >-->
|
||||
<!-- 移除-->
|
||||
<!-- </Button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </TableColumn>-->
|
||||
<!-- </Table>-->
|
||||
|
||||
<!-- <!– 表单属性 –>-->
|
||||
<!-- <Divider key="property-divider" />-->
|
||||
<!-- <p class="listener-filed__title" key="property-title">-->
|
||||
<!-- <span><Icon icon="ep:menu" />字段属性列表:</span>-->
|
||||
<!-- <Button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
|
||||
<!-- >添加属性</Button-->
|
||||
<!-- >-->
|
||||
<!-- </p>-->
|
||||
<!-- <Table :data-source="fieldPropertiesList" key="property-table" :scroll="{ y: 240 }" bordered>-->
|
||||
<!-- <TableColumn title="序号" width="50px" type="index" />-->
|
||||
<!-- <TableColumn title="属性编号" dataIndex="id" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="属性值" dataIndex="value" width="100px" :ellipsis="true" />-->
|
||||
<!-- <TableColumn title="操作" width="90px">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
|
||||
<!-- >-->
|
||||
<!-- 编辑-->
|
||||
<!-- </Button>-->
|
||||
<!-- <Divider type="vertical" />-->
|
||||
<!-- <Button-->
|
||||
<!-- type="link"-->
|
||||
<!-- danger-->
|
||||
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
|
||||
<!-- >-->
|
||||
<!-- 移除-->
|
||||
<!-- </Button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </TableColumn>-->
|
||||
<!-- </Table>-->
|
||||
|
||||
<!-- <!– 底部按钮 –>-->
|
||||
<!-- <div class="element-drawer__button">-->
|
||||
<!-- <Button>取 消</Button>-->
|
||||
<!-- <Button type="primary" @click="saveField">保 存</Button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </Drawer>-->
|
||||
|
||||
<!-- <Modal-->
|
||||
<!-- v-model:open="fieldOptionModelVisible"-->
|
||||
<!-- :title="optionModelTitle"-->
|
||||
<!-- width="600px"-->
|
||||
<!-- destroyOnClose-->
|
||||
<!-- >-->
|
||||
<!-- <Form :model="fieldOptionForm" :label-col="{ style: { width: '96px' } }">-->
|
||||
<!-- <FormItem label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
|
||||
<!-- <Input v-model:value="fieldOptionForm.id" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
|
||||
<!-- <Input v-model:value="fieldOptionForm.name" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
|
||||
<!-- <Input v-model:value="fieldOptionForm.config" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
|
||||
<!-- <Input v-model:value="fieldOptionForm.value" allowClear />-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- </Form>-->
|
||||
<!-- <template #footer>-->
|
||||
<!-- <Button @click="fieldOptionModelVisible = false">取 消</Button>-->
|
||||
<!-- <Button type="primary" @click="saveFieldOption">确 定</Button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </Modal>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -32,7 +32,7 @@ const props = defineProps({
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const prefix = inject('prefix');
|
||||
const prefix = inject<string>('prefix', 'flowable');
|
||||
const elementListenersList = ref<any[]>([]); // 监听器列表
|
||||
const listenerForm = ref<any>({}); // 监听器详情表单
|
||||
const fieldsListOfListener = ref<any[]>([]);
|
||||
|
||||
@@ -30,7 +30,7 @@ interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const prefix = inject<string>('prefix');
|
||||
const prefix = inject<string>('prefix', 'flowable');
|
||||
|
||||
const elementListenersList = ref<any[]>([]);
|
||||
const listenerEventTypeObject = ref(eventType);
|
||||
|
||||
@@ -61,8 +61,8 @@ interface LoopInstanceForm {
|
||||
}
|
||||
|
||||
const loopInstanceForm = ref<LoopInstanceForm>({});
|
||||
const bpmnElement = ref<any>(null);
|
||||
const multiLoopInstance = ref<any>(null);
|
||||
const bpmnElement = ref<any | null>(null);
|
||||
const multiLoopInstance = ref<any | null>(null);
|
||||
declare global {
|
||||
interface Window {
|
||||
bpmnInstances?: () => any;
|
||||
@@ -276,7 +276,7 @@ const approveMethod = ref<ApproveMethodType | undefined>();
|
||||
const approveRatio = ref<number>(100);
|
||||
const otherExtensions = ref<any[]>([]);
|
||||
const getElementLoopNew = (): void => {
|
||||
if (props.type === 'UserTask') {
|
||||
if (props.type === 'UserTask' && bpmnElement.value) {
|
||||
const loopCharacteristics =
|
||||
bpmnElement.value.businessObject?.loopCharacteristics;
|
||||
const extensionElements =
|
||||
@@ -320,6 +320,9 @@ const onApproveRatioChange = (): void => {
|
||||
updateLoopCharacteristics();
|
||||
};
|
||||
const updateLoopCharacteristics = (): void => {
|
||||
if (!bpmnElement.value) {
|
||||
return;
|
||||
}
|
||||
// 根据ApproveMethod生成multiInstanceLoopCharacteristics节点
|
||||
if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
@@ -367,9 +370,11 @@ const updateLoopCharacteristics = (): void => {
|
||||
body: `\${ nrOfCompletedInstances >= nrOfInstances }`,
|
||||
});
|
||||
}
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
loopCharacteristics: toRaw(multiLoopInstance.value),
|
||||
});
|
||||
if (multiLoopInstance.value) {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
loopCharacteristics: toRaw(multiLoopInstance.value),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 添加ApproveMethod到ExtensionElements
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { inject, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { confirm, useVbenModal } from '@vben/common-ui';
|
||||
@@ -151,7 +153,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
} as VxeTableGridOptions<{ name: string; value: string }>,
|
||||
});
|
||||
|
||||
const [FieldModal, fieldModalApi] = useVbenModal({
|
||||
|
||||
@@ -1,7 +1,35 @@
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
interface ListenerFieldOptions {
|
||||
expression?: string;
|
||||
fieldType: string;
|
||||
name: string;
|
||||
string?: string;
|
||||
}
|
||||
|
||||
interface ListenerOptions {
|
||||
class?: string;
|
||||
delegateExpression?: string;
|
||||
event?: string;
|
||||
eventDefinitionType?: string;
|
||||
eventTimeDefinitions?: string;
|
||||
expression?: string;
|
||||
fields?: ListenerFieldOptions[];
|
||||
id?: string;
|
||||
listenerType?: string;
|
||||
resource?: string;
|
||||
scriptFormat?: string;
|
||||
scriptType?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
// 创建监听器实例
|
||||
export function createListenerObject(options, isTask, prefix) {
|
||||
const listenerObj = Object.create(null);
|
||||
export function createListenerObject(
|
||||
options: ListenerOptions,
|
||||
isTask: boolean,
|
||||
prefix: string,
|
||||
) {
|
||||
const listenerObj: Record<string, any> = Object.create(null);
|
||||
listenerObj.event = options.event;
|
||||
isTask && (listenerObj.id = options.id); // 任务监听器特有的 id 字段
|
||||
switch (options.listenerType) {
|
||||
@@ -52,7 +80,10 @@ export function createListenerObject(options, isTask, prefix) {
|
||||
}
|
||||
|
||||
// 创建 监听器的注入字段 实例
|
||||
export function createFieldObject(option, prefix) {
|
||||
export function createFieldObject(
|
||||
option: ListenerFieldOptions,
|
||||
prefix: string,
|
||||
) {
|
||||
const { name, fieldType, string, expression } = option;
|
||||
const fieldConfig =
|
||||
fieldType === 'string' ? { name, string } : { name, expression };
|
||||
@@ -60,7 +91,7 @@ export function createFieldObject(option, prefix) {
|
||||
}
|
||||
|
||||
// 创建脚本实例
|
||||
export function createScriptObject(options, prefix) {
|
||||
export function createScriptObject(options: ListenerOptions, prefix: string) {
|
||||
const { scriptType, scriptFormat, value, resource } = options;
|
||||
const scriptConfig =
|
||||
scriptType === 'inlineScript'
|
||||
@@ -70,7 +101,7 @@ export function createScriptObject(options, prefix) {
|
||||
}
|
||||
|
||||
// 更新元素扩展属性
|
||||
export function updateElementExtensions(element, extensionList) {
|
||||
export function updateElementExtensions(element: any, extensionList: any[]) {
|
||||
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||
values: extensionList,
|
||||
});
|
||||
|
||||
@@ -64,6 +64,11 @@ const currentNode = useWatchNode(props);
|
||||
/** 节点名称配置 */
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.CHILD_PROCESS_NODE);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('child');
|
||||
// 子流程表单配置
|
||||
@@ -181,6 +186,12 @@ const multiFormFieldOptions = computed(() => {
|
||||
(item) => item.type === 'select' || item.type === 'checkbox',
|
||||
);
|
||||
});
|
||||
const multiInstanceSourceNumber = computed({
|
||||
get: () => Number(configForm.value.multiInstanceSource || 1),
|
||||
set: (value?: number) => {
|
||||
configForm.value.multiInstanceSource = String(value || '');
|
||||
},
|
||||
});
|
||||
const childFormFieldOptions = ref<any[]>([]);
|
||||
|
||||
/** 保存配置 */
|
||||
@@ -389,7 +400,7 @@ onMounted(async () => {
|
||||
<div class="config-header">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
type="text"
|
||||
class="focus:border-blue-500 focus:shadow-[0_0_0_2px_rgba(24,144,255,0.2)] focus:outline-none"
|
||||
@blur="changeNodeName()"
|
||||
@@ -776,7 +787,7 @@ onMounted(async () => {
|
||||
]"
|
||||
>
|
||||
<InputNumber
|
||||
v-model:value="configForm.multiInstanceSource"
|
||||
v-model:value="multiInstanceSourceNumber"
|
||||
:min="1"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@@ -74,6 +74,10 @@ const currentNode = useWatchNode(props);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.COPY_TASK_NODE);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
|
||||
@@ -208,7 +212,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
<div class="config-header">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
type="text"
|
||||
class="focus:border-blue-500 focus:shadow-[0_0_0_2px_rgba(24,144,255,0.2)] focus:outline-none"
|
||||
@blur="changeNodeName()"
|
||||
|
||||
@@ -44,6 +44,11 @@ const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.DELAY_TIMER_NODE);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
// 抄送人表单配置
|
||||
const formRef = ref(); // 表单 Ref
|
||||
|
||||
@@ -154,7 +159,7 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
||||
<div class="flex items-center">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
type="text"
|
||||
class="mr-2 w-48"
|
||||
@blur="changeNodeName()"
|
||||
|
||||
@@ -103,21 +103,21 @@ function changeConditionType() {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteConditionGroup(conditions: any, index: number) {
|
||||
conditions.splice(index, 1);
|
||||
function deleteConditionGroup(conditions: any, index: number | string) {
|
||||
conditions.splice(Number(index), 1);
|
||||
}
|
||||
|
||||
function deleteConditionRule(condition: any, index: number) {
|
||||
condition.rules.splice(index, 1);
|
||||
function deleteConditionRule(condition: any, index: number | string) {
|
||||
condition.rules.splice(Number(index), 1);
|
||||
}
|
||||
|
||||
function addConditionRule(condition: any, index: number) {
|
||||
function addConditionRule(condition: any, index: number | string) {
|
||||
const rule = {
|
||||
opCode: '==',
|
||||
leftSide: undefined,
|
||||
rightSide: '',
|
||||
};
|
||||
condition.rules.splice(index + 1, 0, rule);
|
||||
condition.rules.splice(Number(index) + 1, 0, rule);
|
||||
}
|
||||
|
||||
function addConditionGroup(conditions: any) {
|
||||
|
||||
@@ -50,9 +50,9 @@ function addHttpResponseSetting(responseSetting: Record<string, string>[]) {
|
||||
/** 删除 HTTP 请求返回值设置项 */
|
||||
function deleteHttpResponseSetting(
|
||||
responseSetting: Record<string, string>[],
|
||||
index: number,
|
||||
index: number | string,
|
||||
) {
|
||||
responseSetting.splice(index, 1);
|
||||
responseSetting.splice(Number(index), 1);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
|
||||
@@ -66,7 +66,7 @@ defineExpose({ validate });
|
||||
:key="listenerIdx"
|
||||
class="pl-2"
|
||||
>
|
||||
<Divider orientation="left">
|
||||
<Divider title-placement="left">
|
||||
<TypographyText tag="b" size="large">
|
||||
{{ listener.name }}
|
||||
</TypographyText>
|
||||
|
||||
@@ -41,6 +41,11 @@ const currentNode = useWatchNode(props);
|
||||
/** 节点名称 */
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.ROUTER_BRANCH_NODE);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
const routerGroups = ref<RouterSetting[]>([]);
|
||||
const nodeOptions = ref<any[]>([]);
|
||||
const conditionRef = ref<any[]>([]);
|
||||
@@ -202,7 +207,7 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<Input
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="mr-2 w-48"
|
||||
|
||||
@@ -53,6 +53,11 @@ const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.START_USER_NODE);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
|
||||
@@ -144,7 +149,7 @@ defineExpose({ showStartUserNodeConfig });
|
||||
<template #title>
|
||||
<div class="config-header">
|
||||
<Input
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="focus:border-blue-500 focus:shadow-[0_0_0_2px_rgba(24,144,255,0.2)] focus:outline-none"
|
||||
|
||||
@@ -139,7 +139,7 @@ function recursiveFindParentNode(
|
||||
<Button
|
||||
v-else
|
||||
class="branch-node-add"
|
||||
color="#626aef"
|
||||
:style="{ borderColor: '#626aef', color: '#626aef' }"
|
||||
@click="addCondition"
|
||||
plain
|
||||
>
|
||||
|
||||
@@ -39,6 +39,10 @@ const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
BpmNodeTypeEnum.TRIGGER_NODE,
|
||||
);
|
||||
|
||||
function setInputRef(el: unknown) {
|
||||
inputRef.value = el as HTMLInputElement | null;
|
||||
}
|
||||
|
||||
const nodeSetting = ref();
|
||||
// 打开节点配置
|
||||
function openNodeConfig() {
|
||||
@@ -68,7 +72,7 @@ function deleteNode() {
|
||||
<span class="iconfont icon-trigger"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
:ref="setInputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 移动端流程表单展示页面 - Ant Design Vue 版本
|
||||
* 使用 @form-create/antdv-next 渲染表单
|
||||
* 用于 UniApp 通过 iframe/webview 嵌入
|
||||
*
|
||||
* URL 参数说明:
|
||||
* - type: 环境类型(必填)'miniapp' 小程序(微信/支付宝/百度等) | 'h5' H5
|
||||
* - processInstanceId: 流程实例ID(查看已有流程时使用)
|
||||
* - taskId: 任务ID(可选)
|
||||
* - activityId: 活动节点ID(可选)
|
||||
* - token: 访问令牌(用于 API 认证)
|
||||
*/
|
||||
import { computed, nextTick, onMounted, ref, toRaw } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { BpmFieldPermissionType, BpmModelFormType } from '@vben/constants';
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { Button, Empty, Spin } from 'antdv-next';
|
||||
|
||||
import { getApprovalDetail } from '#/api/bpm/processInstance';
|
||||
import { setConfAndFields2 } from '#/components/form-create';
|
||||
|
||||
type EnvType = 'h5' | 'miniapp'; // 环境类型
|
||||
|
||||
// UniApp WebView 类型声明
|
||||
interface UniWebView {
|
||||
postMessage: (options: { data: any }, targetOrigin?: string) => void;
|
||||
getEnv: (callback: (res: any) => void) => void;
|
||||
navigateTo: (options: {
|
||||
fail?: () => void;
|
||||
success?: () => void;
|
||||
url: string;
|
||||
}) => void;
|
||||
navigateBack: (options?: { delta?: number }) => void;
|
||||
switchTab: (options: { url: string }) => void;
|
||||
reLaunch: (options: { url: string }) => void;
|
||||
redirectTo: (options: { url: string }) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
uni?: UniWebView;
|
||||
}
|
||||
}
|
||||
|
||||
defineOptions({ name: 'BpmMobileFormPreview' });
|
||||
|
||||
const route = useRoute();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const envType = ref<EnvType>('h5'); // 环境类型
|
||||
const loading = ref(true); // 页面加载状态
|
||||
const error = ref<null | string>(null);
|
||||
|
||||
const processInstance = ref<any>(null);
|
||||
const processDefinition = ref<any>(null);
|
||||
|
||||
const detailForm = ref<{
|
||||
option: any;
|
||||
rule: any[];
|
||||
value: Record<string, any>;
|
||||
}>({
|
||||
option: {},
|
||||
rule: [],
|
||||
value: {},
|
||||
}); // 流程实例的表单详情
|
||||
const fApi = ref<any>(null); // form-create API 引用
|
||||
const fieldPermissions = ref<Record<string, string>>({}); // 字段权限
|
||||
|
||||
// 是否有表单内容
|
||||
const hasFormContent = computed(() => {
|
||||
return detailForm.value.rule && detailForm.value.rule.length > 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化 Token
|
||||
* 从 URL 参数获取 token 并设置到 store
|
||||
*/
|
||||
function initToken() {
|
||||
const token = route.query.token as string;
|
||||
if (token) {
|
||||
accessStore.setAccessToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并初始化环境类型
|
||||
*/
|
||||
function initEnvType(): boolean {
|
||||
const type = route.query.type as string;
|
||||
|
||||
if (!type) {
|
||||
error.value = '缺少必填参数: type';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type !== 'h5' && type !== 'miniapp') {
|
||||
error.value = 'type 参数值无效,必须是 h5 或 miniapp';
|
||||
return false;
|
||||
}
|
||||
envType.value = type as EnvType;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批详情
|
||||
*/
|
||||
async function getDetail() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const processInstanceId = route.query.processInstanceId as string;
|
||||
const taskId = route.query.taskId as string;
|
||||
const activityId = route.query.activityId as string;
|
||||
|
||||
if (!processInstanceId) {
|
||||
throw new Error('缺少流程实例ID参数');
|
||||
}
|
||||
|
||||
const data = await getApprovalDetail({
|
||||
processInstanceId,
|
||||
taskId,
|
||||
activityId,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new Error('查询不到审批详情信息');
|
||||
}
|
||||
|
||||
if (!data.processDefinition || !data.processInstance) {
|
||||
throw new Error('查询不到流程信息');
|
||||
}
|
||||
|
||||
processInstance.value = data.processInstance;
|
||||
processDefinition.value = data.processDefinition;
|
||||
|
||||
// 设置普通表单信息
|
||||
if (data.processDefinition.formType === BpmModelFormType.NORMAL) {
|
||||
if (detailForm.value.rule?.length > 0) {
|
||||
// 避免刷新 form-create 显示不了
|
||||
detailForm.value.value = processInstance.value.formVariables;
|
||||
} else {
|
||||
setConfAndFields2(
|
||||
detailForm,
|
||||
processDefinition.value.formConf,
|
||||
processDefinition.value.formFields,
|
||||
processInstance.value.formVariables,
|
||||
);
|
||||
}
|
||||
await nextTick();
|
||||
fApi.value?.btn.show(false);
|
||||
fApi.value?.resetBtn.show(false);
|
||||
fApi.value?.disabled(true);
|
||||
// 设置表单字段权限
|
||||
if (data.formFieldsPermission) {
|
||||
Object.keys(data.formFieldsPermission).forEach((item) => {
|
||||
setFieldPermission(item, data.formFieldsPermission[item]);
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向父页面发送消息
|
||||
* 根据环境类型选择不同的通信方式
|
||||
*/
|
||||
function postMessageToParent(message: { data: any; type: string }) {
|
||||
const messageData = {
|
||||
source: 'bpm-mobile-form',
|
||||
type: message.type,
|
||||
data: message.data,
|
||||
};
|
||||
|
||||
// 小程序环境:使用 uni.postMessage
|
||||
if (envType.value === 'miniapp') {
|
||||
if (window.uni?.postMessage) {
|
||||
// 传递的消息信息,必须写在 data 对象中
|
||||
window.uni.postMessage({ data: message.data }, window.location.origin);
|
||||
} else {
|
||||
console.error('小程序环境下 uni 对象未定义');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// H5 环境:使用 window.postMessage
|
||||
if (envType.value === 'h5' && window.parent !== window) {
|
||||
window.parent.postMessage(messageData, '*');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地克隆对象,移除不可序列化的属性
|
||||
*/
|
||||
function safeClone(obj: any): any {
|
||||
try {
|
||||
// 先使用 toRaw 移除 Vue 的响应式代理
|
||||
const raw = toRaw(obj);
|
||||
// 使用 JSON 序列化来移除函数、DOM 元素等不可序列化的内容
|
||||
// eslint-disable-next-line unicorn/prefer-structured-clone
|
||||
return JSON.parse(JSON.stringify(raw));
|
||||
} catch (error) {
|
||||
console.error('克隆对象失败:', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置表单权限 */
|
||||
function setFieldPermission(field: string, permission: string) {
|
||||
fieldPermissions.value[field] = permission;
|
||||
if (permission === BpmFieldPermissionType.READ) {
|
||||
fApi.value?.disabled(true, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.WRITE) {
|
||||
fApi.value?.disabled(false, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.NONE) {
|
||||
fApi.value?.hidden(true, field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮点击事件
|
||||
* 获取表单数据并发送给父页面
|
||||
*/
|
||||
function handleConfirm() {
|
||||
// 获取最新的表单值(转换为普通对象,避免 Proxy 序列化问题)
|
||||
const rawValue = detailForm.value.value;
|
||||
const currentValue = safeClone(rawValue);
|
||||
|
||||
// 发送表单数据给父页面
|
||||
postMessageToParent({
|
||||
type: 'FORM_SUBMIT',
|
||||
data: {
|
||||
formValue: currentValue,
|
||||
fieldPermissions: safeClone(fieldPermissions.value),
|
||||
processInstanceId: route.query.processInstanceId,
|
||||
taskId: route.query.taskId,
|
||||
},
|
||||
});
|
||||
window.uni?.navigateBack();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 验证环境类型
|
||||
if (!initEnvType()) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 先加载微信 JSSDK(微信小程序需要)
|
||||
const wxScript = document.createElement('script');
|
||||
wxScript.type = 'text/javascript';
|
||||
wxScript.src = 'https://res.wx.qq.com/open/js/jweixin-1.4.0.js';
|
||||
|
||||
wxScript.addEventListener('load', () => {
|
||||
// 2. 微信 SDK 加载完成后,加载 UniApp WebView SDK
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
// 所有 SDK 加载完成后初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'UniApp WebView SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
wxScript.addEventListener('error', () => {
|
||||
// 微信 SDK 加载失败,尝试只加载 UniApp SDK(可能是其他小程序)
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
document.head.append(wxScript);
|
||||
|
||||
// 初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
function initApp() {
|
||||
// 设置主题为 light 模式
|
||||
updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 初始化 token
|
||||
initToken();
|
||||
|
||||
// 加载数据
|
||||
if (route.query.processInstanceId) {
|
||||
getDetail();
|
||||
} else {
|
||||
loading.value = false;
|
||||
error.value = '缺少必要参数:processInstanceId';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-form-preview-antd">
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<Spin size="large" description="加载中..." />
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<Empty v-else-if="error" :description="error" />
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<template v-else>
|
||||
<!-- 有表单规则时渲染 form-create -->
|
||||
<div v-if="hasFormContent" class="mt-4">
|
||||
<form-create
|
||||
v-model="detailForm.value"
|
||||
v-model:api="fApi"
|
||||
:option="detailForm.option"
|
||||
:rule="detailForm.rule"
|
||||
/>
|
||||
|
||||
<!-- 确定按钮 -->
|
||||
<div class="form-footer">
|
||||
<Button type="primary" size="large" block @click="handleConfirm">
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无表单内容时显示空状态 -->
|
||||
<Empty v-else description="暂无表单内容" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 响应式适配 */
|
||||
@media (max-width: 768px) {
|
||||
.mobile-form-preview-antd {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-form-preview-antd {
|
||||
min-height: 100px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
box-shadow: 0 -2px 8px rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.form-footer :deep(.ant-btn) {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -20,20 +20,22 @@ import {
|
||||
|
||||
import { getForm } from '#/api/bpm/form';
|
||||
import { setConfAndFields2 } from '#/components/form-create';
|
||||
type Rule = any;
|
||||
const props = defineProps({
|
||||
formList: {
|
||||
type: Array<BpmFormApi.Form>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
type FormCreateRule = {
|
||||
[key: string]: unknown;
|
||||
props?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
defineProps<{
|
||||
formList: BpmFormApi.Form[];
|
||||
}>();
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelData = defineModel<any>(); // 创建本地数据副本
|
||||
const formPreview = ref({
|
||||
formData: {} as any,
|
||||
rule: [],
|
||||
rule: [] as FormCreateRule[],
|
||||
option: {
|
||||
submitBtn: false,
|
||||
resetBtn: false,
|
||||
@@ -41,7 +43,7 @@ const formPreview = ref({
|
||||
},
|
||||
}); // 表单预览数据
|
||||
|
||||
const rules: Record<string, Rule[]> = {
|
||||
const rules: Record<string, any[]> = {
|
||||
formType: [{ required: true, message: '表单类型不能为空', trigger: 'blur' }],
|
||||
formId: [{ required: true, message: '流程表单不能为空', trigger: 'blur' }],
|
||||
formCustomCreatePath: [
|
||||
@@ -60,7 +62,7 @@ watch(
|
||||
const data = await getForm(newFormId);
|
||||
setConfAndFields2(formPreview.value, data.conf, data.fields);
|
||||
// 设置只读
|
||||
formPreview.value.rule.forEach((item: any) => {
|
||||
formPreview.value.rule.forEach((item) => {
|
||||
item.props = { ...item.props, disabled: true };
|
||||
});
|
||||
} else {
|
||||
@@ -109,7 +111,7 @@ defineExpose({ validate });
|
||||
<Select
|
||||
v-model:value="modelData.formId"
|
||||
allow-clear
|
||||
:options="props.formList"
|
||||
:options="formList"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@@ -28,10 +28,14 @@ const { query } = useRoute();
|
||||
const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const processTimeLineLoading = ref(false); // 审批流的加载中
|
||||
|
||||
type LeaveCreateData = BpmOALeaveApi.Leave & {
|
||||
startUserSelectAssignees?: Record<string, number[]>;
|
||||
};
|
||||
|
||||
const processDefineKey = 'oa_leave'; // 流程定义 Key
|
||||
const startUserSelectTasks = ref<any>([]); // 发起人需要选择审批人的用户任务列表
|
||||
const startUserSelectAssignees = ref<any>({}); // 发起人选择审批人的数据
|
||||
const tempStartUserSelectAssignees = ref<any>({}); // 历史发起人选择审批人的数据,用于每次表单变更时,临时保存
|
||||
const startUserSelectTasks = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // 发起人需要选择审批人的用户任务列表
|
||||
const startUserSelectAssignees = ref<Record<string, number[]>>({}); // 发起人选择审批人的数据
|
||||
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); // 历史发起人选择审批人的数据,用于每次表单变更时,临时保存
|
||||
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // 审批节点信息
|
||||
const processDefinitionId = ref('');
|
||||
|
||||
@@ -65,23 +69,21 @@ async function onSubmit() {
|
||||
// 1.2 审批相关:校验指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
for (const userTask of startUserSelectTasks.value) {
|
||||
if (
|
||||
Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
|
||||
startUserSelectAssignees.value[userTask.id].length === 0
|
||||
) {
|
||||
const assignees = startUserSelectAssignees.value[userTask.id];
|
||||
if (Array.isArray(assignees) && assignees.length === 0) {
|
||||
return message.warning(`请选择${userTask.name}的审批人`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as BpmOALeaveApi.Leave;
|
||||
const data = (await formApi.getValues()) as LeaveCreateData;
|
||||
// 审批相关:设置指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
data.startUserSelectAssignees = startUserSelectAssignees.value;
|
||||
}
|
||||
// 格式化开始时间和结束时间的值
|
||||
const submitData: BpmOALeaveApi.Leave = {
|
||||
const submitData: LeaveCreateData = {
|
||||
...data,
|
||||
startTime: Number(data.startTime),
|
||||
endTime: Number(data.endTime),
|
||||
@@ -144,11 +146,10 @@ async function getApprovalDetail() {
|
||||
// 恢复之前的选择审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
for (const node of startUserSelectTasks.value) {
|
||||
startUserSelectAssignees.value[node.id] =
|
||||
tempStartUserSelectAssignees.value[node.id] &&
|
||||
tempStartUserSelectAssignees.value[node.id].length > 0
|
||||
? tempStartUserSelectAssignees.value[node.id]
|
||||
: [];
|
||||
const tempAssignees = tempStartUserSelectAssignees.value[node.id];
|
||||
startUserSelectAssignees.value[node.id] = tempAssignees?.length
|
||||
? tempAssignees
|
||||
: [];
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -157,8 +158,8 @@ async function getApprovalDetail() {
|
||||
}
|
||||
|
||||
/** 审批相关:选择发起人 */
|
||||
function selectUserConfirm(id: string, userList: any[]) {
|
||||
startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id);
|
||||
function selectUserConfirm(id: string, userList: Array<{ id: number }>) {
|
||||
startUserSelectAssignees.value[id] = userList.map((item) => item.id);
|
||||
}
|
||||
|
||||
/** 获取请假数据,用于重新发起时自动填充 */
|
||||
|
||||
@@ -26,7 +26,7 @@ const queryId = computed(() => query.id as string);
|
||||
const [Descriptions] = useDescription({
|
||||
bordered: true,
|
||||
column: 1,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailFormSchema(),
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const [Grid] = useVbenVxeGrid({
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
} as VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpression>,
|
||||
});
|
||||
|
||||
// 配置 Modal
|
||||
@@ -88,7 +88,7 @@ function useGridFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
];
|
||||
}
|
||||
function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
function useGridColumns(): VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpression>['columns'] {
|
||||
return [
|
||||
{ field: 'name', title: '名字', minWidth: 160 },
|
||||
{ field: 'expression', title: '表达式', minWidth: 260 },
|
||||
|
||||
@@ -73,13 +73,12 @@ const detailForm = ref<ProcessFormData>({
|
||||
const fApi = ref<any>();
|
||||
|
||||
const startUserSelectTasks = ref<UserTask[]>([]);
|
||||
const startUserSelectAssignees = ref<Record<string, string[]>>({});
|
||||
const tempStartUserSelectAssignees = ref<Record<string, string[]>>({});
|
||||
const startUserSelectAssignees = ref<Record<string, number[]>>({});
|
||||
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({});
|
||||
|
||||
const bpmnXML = ref<string | undefined>(undefined);
|
||||
const simpleJson = ref<string | undefined>(undefined);
|
||||
|
||||
const timelineRef = ref<any>();
|
||||
const activeTab = ref('form');
|
||||
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]);
|
||||
const processInstanceStartLoading = ref(false);
|
||||
@@ -315,7 +314,6 @@ defineExpose({ initProcessInfo });
|
||||
</Col>
|
||||
<Col :xs="24" :sm="24" :md="6" :lg="6" :xl="6">
|
||||
<ProcessInstanceTimeline
|
||||
ref="timelineRef"
|
||||
:activity-nodes="activityNodes"
|
||||
:show-status-icon="false"
|
||||
@select-user-confirm="selectUserConfirm"
|
||||
|
||||
@@ -33,6 +33,7 @@ watch(
|
||||
view.value = newModelView;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监听 bpmnXml */
|
||||
|
||||
@@ -56,6 +56,7 @@ watch(
|
||||
simpleModel.value = newModelView.simpleModel || {};
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监控模型结构数据 */
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
import type {
|
||||
VxeGridPropTypes,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { BpmProcessListenerApi } from '#/api/bpm/processListener';
|
||||
|
||||
import { ref } from 'vue';
|
||||
@@ -47,7 +50,7 @@ const [Grid] = useVbenVxeGrid({
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
} as VxeTableGridOptions<BpmProcessListenerApi.ProcessListener>,
|
||||
});
|
||||
|
||||
// 配置 Modal
|
||||
|
||||
@@ -16,7 +16,7 @@ const [BaseDescription] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const [SystemDescription] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useFollowUpDetailSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -85,10 +85,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
export function useFormColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'defaultStatus',
|
||||
field: 'endStatus',
|
||||
title: '阶段',
|
||||
minWidth: 100,
|
||||
slots: { default: 'defaultStatus' },
|
||||
slots: { default: 'endStatus' },
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
|
||||
@@ -108,7 +108,7 @@ async function handleAddStatus() {
|
||||
formData.value!.statuses!.splice(-3, 0, {
|
||||
name: '',
|
||||
percent: undefined,
|
||||
} as any);
|
||||
});
|
||||
await nextTick();
|
||||
await gridApi.grid.reloadData(formData.value!.statuses as any);
|
||||
}
|
||||
@@ -152,9 +152,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
<Form class="mx-4">
|
||||
<template #statuses>
|
||||
<Grid class="w-full">
|
||||
<template #defaultStatus="{ row, rowIndex }">
|
||||
<template #endStatus="{ row, rowIndex }">
|
||||
<span>
|
||||
{{ row.defaultStatus ? '结束' : `阶段${rowIndex + 1}` }}
|
||||
{{ row.endStatus ? '结束' : `阶段${rowIndex + 1}` }}
|
||||
</span>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
|
||||
@@ -16,7 +16,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useFollowUpDetailSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useFollowUpDetailSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useFollowUpDetailSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useFollowUpDetailSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -70,17 +70,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
const queryParams: CrmReceivableApi.ReceivablePageParam = {
|
||||
const queryParams = {
|
||||
...(props.customerId ? { customerId: props.customerId } : {}),
|
||||
...(props.customerId && props.contractId
|
||||
? { contractId: props.contractId }
|
||||
: {}),
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
};
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId;
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId;
|
||||
queryParams.contractId = props.contractId;
|
||||
}
|
||||
return await getReceivablePageByCustomer(queryParams);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailSystemSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,7 @@ const [BaseDescriptions] = useDescription({
|
||||
title: '基本信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailBaseSchema(),
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ const [SystemDescriptions] = useDescription({
|
||||
title: '系统信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
class: 'mx-4',
|
||||
classes: { root: 'mx-4' },
|
||||
schema: useDetailSystemSchema(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -179,7 +179,7 @@ async function handleSubmitApply() {
|
||||
</template>
|
||||
</Input>
|
||||
|
||||
<Spin :spinning="loading" wrapper-class-name="w-full">
|
||||
<Spin :spinning="loading" :classes="{ root: 'w-full' }">
|
||||
<div class="h-[400px] mt-2.5">
|
||||
<div
|
||||
v-if="visibleUsers.length === 0"
|
||||
|
||||
@@ -170,7 +170,7 @@ function updateLocalResult(id: number, handleResult: number) {
|
||||
:mask="{ closable: false }"
|
||||
class="im-group-request-list__dialog"
|
||||
>
|
||||
<Spin :spinning="loading" wrapper-class-name="w-full">
|
||||
<Spin :spinning="loading" :classes="{ root: 'w-full' }">
|
||||
<div class="flex flex-col gap-3 max-h-[60vh] overflow-y-auto pr-1">
|
||||
<!-- 空态 -->
|
||||
<Empty v-if="!loading && list.length === 0" description="暂无进群申请" />
|
||||
|
||||
@@ -147,7 +147,7 @@ async function handleJoin() {
|
||||
<Popover
|
||||
v-model:open="popoverVisible"
|
||||
placement="bottomLeft"
|
||||
:overlay-style="{ width: '280px' }"
|
||||
:styles="{ root: { width: '280px' } }"
|
||||
trigger="click"
|
||||
>
|
||||
<!-- 胶囊条本体:电话图标 + 文案(含人数)+ 右箭头 -->
|
||||
|
||||
@@ -305,7 +305,7 @@ async function handleDeleteFriend() {
|
||||
</div>
|
||||
<!-- 右上 "..." 菜单:仅 friend 态展示;含「加入/移出黑名单」 + 「删除联系人」 -->
|
||||
<div v-if="relation === 'friend'" class="flex-shrink-0">
|
||||
<Dropdown :trigger="['click']" placement="bottomRight" overlay-class-name="im-user-info__more-menu">
|
||||
<Dropdown :trigger="['click']" placement="bottomRight" :classes="{ root: 'im-user-info__more-menu' }">
|
||||
<div
|
||||
class="flex items-center justify-center w-7 h-7 rounded cursor-pointer hover:bg-[var(--ant-color-fill-secondary)]"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ref, type VNodeRef, watch } from 'vue'
|
||||
import { type VNodeRef, watch } from 'vue'
|
||||
|
||||
/**
|
||||
* 把响应式 MediaStream 挂到 `<video>` / `<audio>` 元素的 srcObject 上;
|
||||
@@ -7,21 +7,34 @@ import { ref, type VNodeRef, watch } from 'vue'
|
||||
export function useMediaStreamElement<T extends HTMLMediaElement>(
|
||||
streamSource: () => MediaStream | null | undefined
|
||||
): VNodeRef {
|
||||
const elRef = ref<T>()
|
||||
const syncStream = (stream = streamSource()) => {
|
||||
if (elRef.value) {
|
||||
elRef.value.srcObject = stream || null
|
||||
let el: T | null = null
|
||||
let currentStream: MediaStream | null | undefined
|
||||
|
||||
const syncStream = () => {
|
||||
if (el) {
|
||||
el.srcObject = currentStream || null
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
streamSource,
|
||||
(stream) => {
|
||||
syncStream(stream)
|
||||
currentStream = stream
|
||||
syncStream()
|
||||
},
|
||||
{ flush: 'post', immediate: true }
|
||||
)
|
||||
return (el) => {
|
||||
elRef.value = el instanceof HTMLMediaElement ? (el as T) : undefined
|
||||
syncStream()
|
||||
|
||||
return (value) => {
|
||||
if (value instanceof HTMLMediaElement) {
|
||||
el = value as T
|
||||
syncStream()
|
||||
return
|
||||
}
|
||||
|
||||
if (el) {
|
||||
el.srcObject = null
|
||||
}
|
||||
el = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ function handleOpenTransferOwner() {
|
||||
v-model:open="namePopoverVisible"
|
||||
trigger="click"
|
||||
placement="leftTop"
|
||||
:overlay-style="{ width: '280px' }"
|
||||
:styles="{ root: { width: '280px' } }"
|
||||
>
|
||||
<div
|
||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--ant-color-fill-tertiary)]"
|
||||
@@ -529,7 +529,7 @@ function handleOpenTransferOwner() {
|
||||
v-model:open="noticePopoverVisible"
|
||||
trigger="click"
|
||||
placement="leftTop"
|
||||
:overlay-style="{ width: '320px' }"
|
||||
:styles="{ root: { width: '320px' } }"
|
||||
>
|
||||
<div
|
||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--ant-color-fill-tertiary)]"
|
||||
@@ -586,7 +586,7 @@ function handleOpenTransferOwner() {
|
||||
v-model:open="groupRemarkPopoverVisible"
|
||||
trigger="click"
|
||||
placement="leftTop"
|
||||
:overlay-style="{ width: '280px' }"
|
||||
:styles="{ root: { width: '280px' } }"
|
||||
>
|
||||
<div
|
||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--ant-color-fill-tertiary)]"
|
||||
@@ -625,7 +625,7 @@ function handleOpenTransferOwner() {
|
||||
v-model:open="remarkPopoverVisible"
|
||||
trigger="click"
|
||||
placement="leftTop"
|
||||
:overlay-style="{ width: '280px' }"
|
||||
:styles="{ root: { width: '280px' } }"
|
||||
>
|
||||
<div
|
||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--ant-color-fill-tertiary)]"
|
||||
|
||||
@@ -145,7 +145,7 @@ function handleGroupCreated(groupId: number) {
|
||||
v-if="!friend"
|
||||
class="flex flex-col items-center justify-center h-full text-13px text-[var(--ant-color-text-placeholder)] bg-[var(--ant-color-bg-container)]"
|
||||
>
|
||||
<Spin tip="加载中..." />
|
||||
<Spin description="加载中..." />
|
||||
</div>
|
||||
<div v-else class="flex flex-col h-full bg-[var(--ant-color-bg-container)]">
|
||||
<div class="flex-1 overflow-y-auto bg-[var(--ant-color-fill-secondary)]">
|
||||
@@ -184,7 +184,7 @@ function handleGroupCreated(groupId: number) {
|
||||
v-model:open="displayNamePopoverVisible"
|
||||
trigger="click"
|
||||
placement="leftTop"
|
||||
:overlay-style="{ width: '280px' }"
|
||||
:styles="{ root: { width: '280px' } }"
|
||||
>
|
||||
<div
|
||||
class="im-conversation-private-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--ant-color-fill-tertiary)]"
|
||||
|
||||
@@ -118,7 +118,7 @@ const onClick = async () => {
|
||||
wrap-class-name="im-material-detail-modal"
|
||||
destroy-on-hidden
|
||||
>
|
||||
<Spin :spinning="detailLoading" wrapper-class-name="w-full">
|
||||
<Spin :spinning="detailLoading" :classes="{ root: 'w-full' }">
|
||||
<div class="material-detail-body max-w-[720px] mx-auto px-5 pt-6 pb-20 min-h-[60vh]">
|
||||
<div class="text-[22px] font-600 leading-[1.4] text-[var(--ant-color-text)] mb-5">
|
||||
{{ payload.title || '' }}
|
||||
|
||||
@@ -569,7 +569,7 @@ function locateMessage(messageId: number) {
|
||||
v-model:open="datePopoverVisible"
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
:overlay-style="{ width: '320px' }"
|
||||
:styles="{ root: { width: '320px' } }"
|
||||
>
|
||||
<span
|
||||
class="im-message-history__tab cursor-pointer"
|
||||
@@ -600,7 +600,7 @@ function locateMessage(messageId: number) {
|
||||
v-model:open="memberPopoverVisible"
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
:overlay-style="{ width: '320px' }"
|
||||
:styles="{ root: { width: '320px' } }"
|
||||
>
|
||||
<span
|
||||
class="im-message-history__tab cursor-pointer"
|
||||
|
||||
@@ -567,9 +567,9 @@ watch(
|
||||
v-if="isPrivate"
|
||||
v-model:open="callPopoverVisible"
|
||||
placement="bottomRight"
|
||||
:overlay-style="{ width: '140px' }"
|
||||
:styles="{ root: { width: '140px' } }"
|
||||
trigger="click"
|
||||
overlay-class-name="message-panel__call-popover"
|
||||
:classes="{ root: 'message-panel__call-popover' }"
|
||||
>
|
||||
<Icon
|
||||
icon="ant-design:phone-outlined"
|
||||
|
||||
@@ -126,7 +126,7 @@ async function loadReadUsers() {
|
||||
v-model:open="popVisible"
|
||||
placement="left"
|
||||
trigger="click"
|
||||
:overlay-style="{ width: '320px' }"
|
||||
:styles="{ root: { width: '320px' } }"
|
||||
@open-change="(open) => open && loadReadUsers()"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -65,7 +65,12 @@ defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer v-model:open="visible" destroy-on-hidden title="群详情" :styles="{ wrapper: { width: '900px' } }">
|
||||
<Drawer
|
||||
v-model:open="visible"
|
||||
destroy-on-hidden
|
||||
title="群详情"
|
||||
:styles="{ wrapper: { width: '900px' } }"
|
||||
>
|
||||
<Descriptions bordered :column="2">
|
||||
<DescriptionsItem label="群编号">{{ detail.id }}</DescriptionsItem>
|
||||
<DescriptionsItem label="群名称">{{ detail.name }}</DescriptionsItem>
|
||||
@@ -77,12 +82,17 @@ defineExpose({ open });
|
||||
<DescriptionsItem label="群主">
|
||||
{{ formatUserLabel(detail.ownerNickname, detail.ownerUserId) }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="成员数">{{ detail.memberCount || 0 }}</DescriptionsItem>
|
||||
<DescriptionsItem label="成员数">
|
||||
{{ detail.memberCount || 0 }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="群状态">
|
||||
<DictTag :type="DICT_TYPE.IM_GROUP_STATUS" :value="detail.status" />
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="封禁状态">
|
||||
<DictTag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="detail.banned" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.INFRA_BOOLEAN_STRING"
|
||||
:value="detail.banned"
|
||||
/>
|
||||
<span v-if="detail.banned" class="ml-2 text-gray-400">
|
||||
{{ detail.bannedReason }}
|
||||
</span>
|
||||
@@ -112,17 +122,23 @@ defineExpose({ open });
|
||||
row-key="userId"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template #bodyCell="{ column, record, text }">
|
||||
<template v-if="column.dataIndex === 'avatar'">
|
||||
<Avatar :src="record.avatar" :size="40">
|
||||
{{ record.nickname?.charAt(0) || '?' }}
|
||||
</Avatar>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'role'">
|
||||
<DictTag :type="DICT_TYPE.IM_GROUP_MEMBER_ROLE" :value="record.role" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IM_GROUP_MEMBER_ROLE"
|
||||
:value="record.role"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'silent'">
|
||||
<DictTag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="record.silent" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.INFRA_BOOLEAN_STRING"
|
||||
:value="record.silent"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="record.status" />
|
||||
@@ -134,7 +150,11 @@ defineExpose({ open });
|
||||
{{ formatDateTimeText(record.quitTime) }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'muteEndTime'">
|
||||
<template v-if="record.muteEndTime && new Date(record.muteEndTime) > new Date()">
|
||||
<template
|
||||
v-if="
|
||||
record.muteEndTime && new Date(record.muteEndTime) > new Date()
|
||||
"
|
||||
>
|
||||
<Tag color="error">禁言中</Tag>
|
||||
<div class="mt-1 text-xs text-gray-400">
|
||||
{{ formatDateTimeText(record.muteEndTime) }}
|
||||
@@ -143,7 +163,7 @@ defineExpose({ open });
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.dataIndex] || '-' }}
|
||||
{{ text || '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
@@ -78,7 +78,7 @@ const [Grid] = useVbenVxeGrid({
|
||||
<template #atUsers="{ row }">
|
||||
<template v-if="row.atUserIds?.length">
|
||||
<span v-for="(userId, index) in row.atUserIds" :key="userId">
|
||||
<span v-if="index > 0">、</span>
|
||||
<span v-if="Number(index) > 0">、</span>
|
||||
<template v-if="userId === IM_AT_ALL_USER_ID">@{{ IM_AT_ALL_NICKNAME }}</template>
|
||||
<template v-else>@{{ row.atUserNicknames?.[index] || userId }}</template>
|
||||
</span>
|
||||
|
||||
@@ -62,7 +62,7 @@ defineExpose({ open });
|
||||
<DescriptionsItem label="@用户" :span="2">
|
||||
<template v-if="detail.atUserIds?.length">
|
||||
<span v-for="(userId, index) in detail.atUserIds" :key="userId">
|
||||
<span v-if="index > 0">、</span>
|
||||
<span v-if="Number(index) > 0">、</span>
|
||||
<template v-if="userId === IM_AT_ALL_USER_ID">
|
||||
@{{ IM_AT_ALL_NICKNAME }}
|
||||
</template>
|
||||
|
||||
@@ -51,21 +51,34 @@ defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer v-model:open="visible" destroy-on-hidden title="通话记录详情" :styles="{ wrapper: { width: '900px' } }">
|
||||
<Drawer
|
||||
v-model:open="visible"
|
||||
destroy-on-hidden
|
||||
title="通话记录详情"
|
||||
:styles="{ wrapper: { width: '900px' } }"
|
||||
>
|
||||
<Descriptions bordered :column="2">
|
||||
<DescriptionsItem label="编号">{{ detail.id }}</DescriptionsItem>
|
||||
<DescriptionsItem label="业务通话编号">{{ detail.room }}</DescriptionsItem>
|
||||
<DescriptionsItem label="业务通话编号">
|
||||
{{ detail.room }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="发起人">
|
||||
{{ formatUserLabel(detail.inviterNickname, detail.inviterUserId) }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="会话类型">
|
||||
<DictTag :type="DICT_TYPE.IM_RTC_CALL_CONVERSATION_TYPE" :value="detail.conversationType" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IM_RTC_CALL_CONVERSATION_TYPE"
|
||||
:value="detail.conversationType"
|
||||
/>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="群">
|
||||
{{ formatGroupLabel(detail.groupName, detail.groupId) }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="媒体类型">
|
||||
<DictTag :type="DICT_TYPE.IM_RTC_CALL_MEDIA_TYPE" :value="detail.mediaType" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IM_RTC_CALL_MEDIA_TYPE"
|
||||
:value="detail.mediaType"
|
||||
/>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="通话状态">
|
||||
<DictTag :type="DICT_TYPE.IM_RTC_CALL_STATUS" :value="detail.status" />
|
||||
@@ -100,18 +113,30 @@ defineExpose({ open });
|
||||
row-key="id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template #bodyCell="{ column, record, text }">
|
||||
<template v-if="column.dataIndex === 'role'">
|
||||
<DictTag :type="DICT_TYPE.IM_RTC_PARTICIPANT_ROLE" :value="record.role" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IM_RTC_PARTICIPANT_ROLE"
|
||||
:value="record.role"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<DictTag :type="DICT_TYPE.IM_RTC_PARTICIPANT_STATUS" :value="record.status" />
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IM_RTC_PARTICIPANT_STATUS"
|
||||
:value="record.status"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="['inviteTime', 'acceptTime', 'leaveTime'].includes(column.dataIndex as string)">
|
||||
{{ formatDateTimeText(record[column.dataIndex]) }}
|
||||
<template
|
||||
v-else-if="
|
||||
['inviteTime', 'acceptTime', 'leaveTime'].includes(
|
||||
column.dataIndex as string,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ formatDateTimeText(text) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.dataIndex] || '-' }}
|
||||
{{ text || '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user