Pre Merge pull request !370 from 芋道源码/migration

pull/370/MERGE
芋道源码 2026-06-22 05:27:10 +00:00 committed by Gitee
commit afe3e2030a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
196 changed files with 1421 additions and 839 deletions

View File

@ -20,7 +20,7 @@
"build:analyze": "pnpm vite build --mode analyze", "build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development", "dev": "pnpm vite --mode development",
"preview": "vite preview", "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": { "imports": {
"#/*": "./src/*" "#/*": "./src/*"

View File

@ -14,7 +14,7 @@ export namespace AiChatConversationApi {
temperature: number; // 温度参数 temperature: number; // 温度参数
maxTokens: number; // 单条回复的最大 Token 数量 maxTokens: number; // 单条回复的最大 Token 数量
maxContexts: number; // 上下文的最大 Message 数量 maxContexts: number; // 上下文的最大 Message 数量
createTime?: Date; // 创建时间 createTime: Date; // 创建时间
systemMessage?: string; // 角色设定 systemMessage?: string; // 角色设定
modelName?: string; // 模型名字 modelName?: string; // 模型名字
roleAvatar?: string; // 角色头像 roleAvatar?: string; // 角色头像

View File

@ -34,15 +34,15 @@ export namespace AiImageApi {
prompt: string; // 提示词 prompt: string; // 提示词
modelId: number; // 模型 modelId: number; // 模型
style: string; // 图像生成的风格 style: string; // 图像生成的风格
width: string; // 图片宽度 width: number; // 图片宽度
height: string; // 图片高度 height: number; // 图片高度
options: object; // 绘制参数Map<String, String> options: object; // 绘制参数Map<String, String>
} }
export interface ImageMidjourneyImagineReqVO { export interface ImageMidjourneyImagineReqVO {
prompt: string; // 提示词 prompt: string; // 提示词
modelId: number; // 模型 modelId: number; // 模型
base64Array?: string[]; // size不能为空 base64Array: string[]; // 参考图 base64 列表
width: string; // 图片宽度 width: string; // 图片宽度
height: string; // 图片高度 height: string; // 图片高度
version: string; // 版本 version: string; // 版本

View File

@ -12,7 +12,6 @@ export namespace BpmOALeaveApi {
startTime: number; startTime: number;
endTime: number; endTime: number;
createTime: Date; createTime: Date;
startUserSelectAssignees?: Record<string, string[]>;
} }
} }

View File

@ -11,15 +11,16 @@ export namespace CrmBusinessStatusApi {
deptNames?: string[]; deptNames?: string[];
creator?: string; creator?: string;
createTime?: Date; createTime?: Date;
statuses?: BusinessStatusType[]; statuses: BusinessStatusType[];
} }
/** 商机状态信息 */ /** 商机状态信息 */
export interface BusinessStatusType { export interface BusinessStatusType {
id?: number; id?: number;
name: string; name: string;
percent: number; percent?: number;
[x: string]: any; endStatus?: number;
key?: string;
} }
} }
@ -43,7 +44,7 @@ export const DEFAULT_STATUSES = [
name: '无效', name: '无效',
percent: 0, percent: 0,
}, },
]; ] satisfies CrmBusinessStatusApi.BusinessStatusType[];
/** 查询商机状态组列表 */ /** 查询商机状态组列表 */
export function getBusinessStatusPage(params: PageParam) { export function getBusinessStatusPage(params: PageParam) {

View File

@ -24,6 +24,18 @@ export namespace InfraCodegenApi {
parentMenuId: number; 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 { export interface CodegenColumn {
id: number; id: number;
@ -54,7 +66,7 @@ export namespace InfraCodegenApi {
/** 代码生成详情 */ /** 代码生成详情 */
export interface CodegenDetail { export interface CodegenDetail {
table: CodegenTable; table: CodegenTableSaveReqVO;
columns: CodegenColumn[]; columns: CodegenColumn[];
} }
@ -66,7 +78,7 @@ export namespace InfraCodegenApi {
/** 更新代码生成请求 */ /** 更新代码生成请求 */
export interface CodegenUpdateReqVO { export interface CodegenUpdateReqVO {
table: any | CodegenTable; table: CodegenTableSaveReqVO;
columns: CodegenColumn[]; columns: CodegenColumn[];
} }

View File

@ -11,7 +11,7 @@ export namespace Demo02CategoryApi {
} }
/** 查询示例分类列表 */ /** 查询示例分类列表 */
export function getDemo02CategoryList(params: any) { export function getDemo02CategoryList(params?: any) {
return requestClient.get<Demo02CategoryApi.Demo02Category[]>( return requestClient.get<Demo02CategoryApi.Demo02Category[]>(
'/infra/demo02-category/list', '/infra/demo02-category/list',
{ params }, { params },

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
} }
} }

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
demo03courses?: Demo03Course[]; demo03courses?: Demo03Course[];
demo03grade?: Demo03Grade; demo03grade?: Demo03Grade;

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
demo03courses?: Demo03Course[]; demo03courses?: Demo03Course[];
demo03grade?: Demo03Grade; demo03grade?: Demo03Grade;

View File

@ -6,11 +6,11 @@ export namespace IoTOtaTaskApi {
/** IoT OTA 升级任务 */ /** IoT OTA 升级任务 */
export interface Task { export interface Task {
id?: number; id?: number;
name?: string; name: string;
description?: string; description?: string;
firmwareId?: number; firmwareId?: number;
status?: number; status?: number;
deviceScope?: number; deviceScope: number;
deviceIds?: number[]; deviceIds?: number[];
deviceTotalCount?: number; deviceTotalCount?: number;
deviceSuccessCount?: number; deviceSuccessCount?: number;

View File

@ -30,8 +30,8 @@ export namespace ThingModelApi {
required?: boolean; required?: boolean;
dataType?: string; dataType?: string;
description?: string; description?: string;
dataSpecs?: any; dataSpecs?: ThingModelDataSpecs;
dataSpecsList?: any[]; dataSpecsList?: ThingModelPropertyDataSpecs[];
} }
/** IoT 物模型服务 */ /** IoT 物模型服务 */
@ -64,8 +64,8 @@ export namespace ThingModelApi {
direction?: string; direction?: string;
paraOrder?: number; paraOrder?: number;
dataType?: string; dataType?: string;
dataSpecs?: any; dataSpecs?: ThingModelDataSpecs;
dataSpecsList?: any[]; dataSpecsList?: ThingModelPropertyDataSpecs[];
} }
/** IoT 物模型 TSL树形响应 */ /** IoT 物模型 TSL树形响应 */
@ -78,19 +78,35 @@ export namespace ThingModelApi {
} }
/** IoT 数据定义(数值型) */ /** 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; min?: number | string;
max?: number | string; max?: number | string;
name?: string;
precise?: string;
required?: boolean;
size?: number | string;
step?: number | string; step?: number | string;
unit?: string; unit?: string;
unitName?: string; unitName?: string;
value?: number | string;
} }
/** IoT 数据定义(数值型) */
export type DataSpecsNumberData = ThingModelDataSpecs;
/** IoT 数据定义(枚举/布尔型) */ /** IoT 数据定义(枚举/布尔型) */
export interface DataSpecsEnumOrBoolData { export type DataSpecsEnumOrBoolData = ThingModelDataSpecs;
value: number | string;
name: string; export type ThingModelPropertyDataSpecs = Property & ThingModelDataSpecs;
}
} }
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */

View File

@ -8,6 +8,7 @@ export namespace MallCombinationActivityApi {
id?: number; // 活动编号 id?: number; // 活动编号
name?: string; // 活动名称 name?: string; // 活动名称
spuId?: number; // 商品 SPU 编号 spuId?: number; // 商品 SPU 编号
spuName?: string; // 商品 SPU 名称
totalLimitCount?: number; // 总限购数量 totalLimitCount?: number; // 总限购数量
singleLimitCount?: number; // 单次限购数量 singleLimitCount?: number; // 单次限购数量
startTime?: Date; // 开始时间 startTime?: Date; // 开始时间
@ -21,7 +22,7 @@ export namespace MallCombinationActivityApi {
limitDuration?: number; // 限制时长 limitDuration?: number; // 限制时长
combinationPrice?: number; // 拼团价格 combinationPrice?: number; // 拼团价格
products: CombinationProduct[]; // 商品列表 products: CombinationProduct[]; // 商品列表
picUrl?: any; picUrl?: string; // 商品图片
} }
/** 拼团活动所需属性 */ /** 拼团活动所需属性 */

View File

@ -26,9 +26,9 @@ export namespace MallRewardActivityApi {
conditionType?: number; // 条件类型 conditionType?: number; // 条件类型
productScope?: number; // 商品范围 productScope?: number; // 商品范围
rules: RewardRule[]; // 优惠规则列表 rules: RewardRule[]; // 优惠规则列表
productScopeValues?: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表 productScopeValues: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表
productCategoryIds?: number[]; // 商品分类编号列表(仅表单使用) productCategoryIds: number[]; // 商品分类编号列表(仅表单使用)
productSpuIds?: number[]; // 商品 SPU 编号列表(仅表单使用) productSpuIds: number[]; // 商品 SPU 编号列表(仅表单使用)
} }
} }

View File

@ -31,7 +31,7 @@ export namespace MallSeckillActivityApi {
totalStock?: number; // 秒杀总库存 totalStock?: number; // 秒杀总库存
seckillPrice?: number; // 秒杀价格 seckillPrice?: number; // 秒杀价格
products?: SeckillProduct[]; // 秒杀商品列表 products?: SeckillProduct[]; // 秒杀商品列表
picUrl?: any; picUrl?: string; // 商品图片
} }
} }

View File

@ -4,20 +4,21 @@ export namespace MallTradeConfigApi {
/** 交易中心配置 */ /** 交易中心配置 */
export interface Config { export interface Config {
id?: number; id?: number;
afterSaleRefundReasons?: string[]; afterSaleRefundReasons: string[];
afterSaleReturnReasons?: string[]; afterSaleReturnReasons: string[];
deliveryExpressFreeEnabled?: boolean; deliveryExpressFreeEnabled: boolean;
deliveryExpressFreePrice?: number; deliveryExpressFreePrice: number;
deliveryPickUpEnabled?: boolean; deliveryPickUpEnabled: boolean;
brokerageEnabled?: boolean; brokerageEnabled?: boolean;
brokerageEnabledCondition?: number; brokerageEnabledCondition?: number;
brokerageBindMode?: number; brokerageBindMode?: number;
brokeragePosterUrls?: string; brokeragePosterUrls: string[];
brokerageFirstPercent?: number; brokerageFirstPercent?: number;
brokerageSecondPercent?: number; brokerageSecondPercent?: number;
brokerageWithdrawMinPrice?: number; brokerageWithdrawMinPrice: number;
brokerageFrozenDays?: number; brokerageFrozenDays: number;
brokerageWithdrawTypes?: string; brokerageWithdrawFeePercent: number;
brokerageWithdrawTypes: number[];
tencentLbsKey?: string; tencentLbsKey?: string;
} }
} }

View File

@ -4,7 +4,7 @@ export namespace MemberConfigApi {
/** 积分设置信息 */ /** 积分设置信息 */
export interface Config { export interface Config {
id?: number; id?: number;
pointTradeDeductEnable: number; pointTradeDeductEnable: boolean;
pointTradeDeductUnitPrice: number; pointTradeDeductUnitPrice: number;
pointTradeDeductMaxPrice: number; pointTradeDeductMaxPrice: number;
pointTradeGivePoint: number; pointTradeGivePoint: number;

View File

@ -10,27 +10,31 @@ export namespace MemberUserApi {
birthday?: number; birthday?: number;
createTime?: number; createTime?: number;
loginDate?: number; loginDate?: number;
loginIp: string; loginIp?: string;
mark: string; mark?: string;
mobile: string; mobile?: string;
email?: string; email?: string;
name?: string; name?: string;
nickname?: string; nickname?: string;
registerIp: string; registerIp?: string;
sex: number; sex?: number;
status: number; status?: number;
areaId?: number; areaId?: number;
areaName?: string; areaName?: string;
levelName: string; tagIds?: number[];
point?: number; groupId?: number;
totalPoint?: number; levelId?: number;
experience?: number; levelName?: null | string;
point?: null | number;
totalPoint?: null | number;
experience?: null | number;
} }
/** 会员用户等级更新信息 */ /** 会员用户等级更新信息 */
export interface UserUpdateLevelReqVO { export interface UserUpdateLevelReqVO {
id: number; id: number;
levelId: number; levelId: number;
reason: string;
} }
/** 会员用户积分更新信息 */ /** 会员用户积分更新信息 */

View File

@ -11,6 +11,12 @@ export namespace MpTagApi {
count?: number; count?: number;
createTime?: Date; createTime?: Date;
} }
/** 标签精简信息 */
export interface SimpleTag {
tagId: number;
name: string;
}
} }
/** 创建公众号标签 */ /** 创建公众号标签 */
@ -46,7 +52,7 @@ export function getTagPage(params: PageParam) {
/** 获取公众号标签精简信息列表 */ /** 获取公众号标签精简信息列表 */
export function getSimpleTagList() { export function getSimpleTagList() {
return requestClient.get<MpTagApi.Tag[]>('/mp/tag/list-all-simple'); return requestClient.get<MpTagApi.SimpleTag[]>('/mp/tag/list-all-simple');
} }
/** 同步公众号标签 */ /** 同步公众号标签 */

View File

@ -3,6 +3,16 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace PayNotifyApi { export namespace PayNotifyApi {
/** 支付通知日志 */
export interface NotifyLog {
id?: number;
status?: number;
notifyTimes?: number;
lastExecuteTime?: Date;
createTime?: Date;
response?: string;
}
/** 支付通知任务 */ /** 支付通知任务 */
export interface NotifyTask { export interface NotifyTask {
id: number; id: number;
@ -20,13 +30,13 @@ export namespace PayNotifyApi {
maxNotifyTimes: number; maxNotifyTimes: number;
createTime: Date; createTime: Date;
updateTime: Date; updateTime: Date;
logs?: any[]; logs?: NotifyLog[];
} }
} }
/** 获得支付通知明细 */ /** 获得支付通知明细 */
export function getNotifyTaskDetail(id: number) { 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}`);
} }
/** 获得支付通知分页 */ /** 获得支付通知分页 */

View File

@ -90,7 +90,7 @@ export const useMallKefuStore = defineStore('mall-kefu', {
}, },
conversationSort() { conversationSort() {
// 按置顶属性和最后消息时间排序 // 按置顶属性和最后消息时间排序
this.conversationList.toSorted((a, b) => { this.conversationList = this.conversationList.toSorted((a, b) => {
// 按照置顶排序,置顶的会在前面 // 按照置顶排序,置顶的会在前面
if (a.adminPinned !== b.adminPinned) { if (a.adminPinned !== b.adminPinned) {
return a.adminPinned ? -1 : 1; return a.adminPinned ? -1 : 1;

View File

@ -90,7 +90,7 @@ async function getChatConversationList() {
// 1.1 // 1.1
conversationList.value = await getChatConversationMyList(); conversationList.value = await getChatConversationMyList();
// 1.2 // 1.2
conversationList.value.toSorted((a, b) => { conversationList.value = conversationList.value.toSorted((a, b) => {
return Number(b.createTime) - Number(a.createTime); return Number(b.createTime) - Number(a.createTime);
}); });
// 1.3 // 1.3

View File

@ -20,8 +20,8 @@ const props = defineProps({
const emits = defineEmits(['onBtnClick', 'onMjBtnClick']); const emits = defineEmits(['onBtnClick', 'onMjBtnClick']);
/** 处理点击事件 */ /** 处理点击事件 */
async function handleButtonClick(type: string, detail: AiImageApi.Image) { async function handleButtonClick(type: string) {
emits('onBtnClick', type, detail); emits('onBtnClick', type, props.detail);
} }
/** 处理 Midjourney 按钮点击事件 */ /** 处理 Midjourney 按钮点击事件 */
@ -79,28 +79,28 @@ onMounted(async () => {
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('download', detail)" @click="handleButtonClick('download')"
> >
<IconifyIcon icon="lucide:download" /> <IconifyIcon icon="lucide:download" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('regeneration', detail)" @click="handleButtonClick('regeneration')"
> >
<IconifyIcon icon="lucide:refresh-cw" /> <IconifyIcon icon="lucide:refresh-cw" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('delete', detail)" @click="handleButtonClick('delete')"
> >
<IconifyIcon icon="lucide:trash" /> <IconifyIcon icon="lucide:trash" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('more', detail)" @click="handleButtonClick('more')"
> >
<IconifyIcon icon="lucide:ellipsis-vertical" /> <IconifyIcon icon="lucide:ellipsis-vertical" />
</Button> </Button>
@ -119,8 +119,8 @@ onMounted(async () => {
<div class="mt-2 flex w-full flex-wrap justify-start"> <div class="mt-2 flex w-full flex-wrap justify-start">
<Button <Button
size="small" size="small"
v-for="(button, index) in detail?.buttons" v-for="button in detail?.buttons"
:key="index" :key="button.customId"
class="m-2 ml-0 min-w-10" class="m-2 ml-0 min-w-10"
@click="handleMidjourneyBtnClick(button)" @click="handleMidjourneyBtnClick(button)"
> >

View File

@ -111,8 +111,8 @@ async function handleGenerateImage() {
prompt: prompt.value, // prompt: prompt.value, //
modelId: matchedModel.id, // 使 modelId: matchedModel.id, // 使
style: style.value, // style: style.value, //
width: imageSize.width, // size width: Number(imageSize.width), // size
height: imageSize.height, // size height: Number(imageSize.height), // size
options: { options: {
style: style.value, // style: style.value, //
}, },

View File

@ -99,6 +99,7 @@ async function handleGenerateImage() {
const req = { const req = {
prompt: prompt.value, prompt: prompt.value,
modelId: matchedModel.id, modelId: matchedModel.id,
base64Array: [],
width: imageSize.width, width: imageSize.width,
height: imageSize.height, height: imageSize.height,
version: selectVersion.value, version: selectVersion.value,

View File

@ -1,16 +1,22 @@
<script lang="ts" setup> <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 { IconifyIcon } from '@vben/icons';
import { formatPast } from '@vben/utils';
import { Image, Slider } from 'ant-design-vue'; import { Image, Slider } from 'ant-design-vue';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicAudioBarIndex' }); 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 audioRef = ref<HTMLAudioElement | null>(null);
const audioProgress = ref(0);
const audioDuration = ref(0);
const audioProps = reactive<any>({ const audioProps = reactive<any>({
autoplay: true, autoplay: true,
paused: false, paused: false,
@ -20,6 +26,17 @@ const audioProps = reactive<any>({
volume: 50, volume: 50,
}); // https://www.runoob.com/tags/ref-av-dom.html }); // 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) { function toggleStatus(type: string) {
audioProps[type] = !audioProps[type]; audioProps[type] = !audioProps[type];
if (type === 'paused' && audioRef.value) { if (type === 'paused' && audioRef.value) {
@ -32,9 +49,40 @@ function toggleStatus(type: string) {
} }
/** 更新播放位置 */ /** 更新播放位置 */
function audioTimeUpdate(args: any) { function audioTimeUpdate() {
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss'); 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> </script>
<template> <template>
@ -48,8 +96,10 @@ function audioTimeUpdate(args: any) {
:width="45" :width="45"
/> />
<div> <div>
<div>{{ currentSong.name }}</div> <div>{{ currentSong.title || '暂无音乐' }}</div>
<div class="text-xs text-gray-400">{{ currentSong.singer }}</div> <div class="text-xs text-gray-400">
{{ currentSong.singer || currentSong.desc }}
</div>
</div> </div>
</div> </div>
<!-- 音频controls --> <!-- 音频controls -->
@ -74,22 +124,25 @@ function audioTimeUpdate(args: any) {
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<span>{{ audioProps.currentTime }}</span> <span>{{ audioProps.currentTime }}</span>
<Slider <Slider
v-model:value="audioProps.duration" v-model:value="audioProgress"
:max="audioDuration"
color="#409eff" color="#409eff"
class="!w-40" class="!w-40"
@change="handleProgressChange"
/> />
<span>{{ audioProps.duration }}</span> <span>{{ audioProps.duration }}</span>
</div> </div>
<!-- 音频 --> <!-- 音频 -->
<audio <audio
v-bind="audioProps" :src="currentAudioUrl"
:autoplay="audioProps.autoplay"
:muted="audioProps.muted"
ref="audioRef" ref="audioRef"
controls controls
v-show="!audioProps" v-show="!audioProps"
@timeupdate="audioTimeUpdate" @timeupdate="audioTimeUpdate"
> @loadedmetadata="audioLoadedMetadata"
<!-- <source :src="audioUrl" /> --> ></audio>
</audio>
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<IconifyIcon <IconifyIcon

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Recordable } from '@vben/types'; import type { Recordable } from '@vben/types';
import type { MusicSong } from './types';
import { provide, ref } from 'vue'; import { provide, ref } from 'vue';
@ -8,14 +9,15 @@ import { Col, Empty, Row, TabPane, Tabs } from 'ant-design-vue';
import audioBar from './audioBar/index.vue'; import audioBar from './audioBar/index.vue';
import songCard from './songCard/index.vue'; import songCard from './songCard/index.vue';
import songInfo from './songInfo/index.vue'; import songInfo from './songInfo/index.vue';
import { currentSongKey } from './types';
defineOptions({ name: 'AiMusicListIndex' }); defineOptions({ name: 'AiMusicListIndex' });
const currentType = ref('mine'); const currentType = ref('mine');
const loading = ref(false); // loading const loading = ref(false); // loading
const currentSong = ref({}); // const currentSong = ref<MusicSong>({}); //
const mySongList = ref<Recordable<any>[]>([]); const mySongList = ref<MusicSong[]>([]);
const squareSongList = ref<Recordable<any>[]>([]); const squareSongList = ref<MusicSong[]>([]);
function generateMusic(_formData: Recordable<any>) { function generateMusic(_formData: Recordable<any>) {
loading.value = true; loading.value = true;
@ -45,7 +47,7 @@ function generateMusic(_formData: Recordable<any>) {
}, 3000); }, 3000);
} }
function setCurrentSong(music: Recordable<any>) { function setCurrentSong(music: MusicSong) {
currentSong.value = music; currentSong.value = music;
} }
@ -53,7 +55,7 @@ defineExpose({
generateMusic, generateMusic,
}); });
provide('currentSong', currentSong); provide(currentSongKey, currentSong);
</script> </script>
<template> <template>

View File

@ -1,22 +1,23 @@
<script lang="ts" setup> <script lang="ts" setup>
import { inject } from 'vue'; import type { MusicSong } from '../types';
import { inject, ref } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { Image } from 'ant-design-vue'; import { Image } from 'ant-design-vue';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicSongCardIndex' }); defineOptions({ name: 'AiMusicSongCardIndex' });
defineProps({ withDefaults(defineProps<{ songInfo?: MusicSong }>(), {
songInfo: { songInfo: () => ({}),
type: Object,
default: () => ({}),
},
}); });
const emits = defineEmits(['play']); const emits = defineEmits(['play']);
const currentSong = inject<any>('currentSong', {}); const currentSong = inject(currentSongKey, ref<MusicSong>({}));
function playSong() { function playSong() {
emits('play'); emits('play');

View File

@ -1,11 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { inject } from 'vue'; import type { MusicSong } from '../types';
import { inject, ref } from 'vue';
import { Button, Card, Image } from 'ant-design-vue'; import { Button, Card, Image } from 'ant-design-vue';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicSongInfoIndex' }); defineOptions({ name: 'AiMusicSongInfoIndex' });
const currentSong = inject<any>('currentSong', {}); const currentSong = inject(currentSongKey, ref<MusicSong>({}));
</script> </script>
<template> <template>

View 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');

View File

@ -245,6 +245,9 @@ const moddleExtensions = computed(() => {
const initBpmnModeler = () => { const initBpmnModeler = () => {
if (bpmnModeler) return; if (bpmnModeler) return;
const data: any = document.querySelector('#bpmnCanvas'); const data: any = document.querySelector('#bpmnCanvas');
if (!data) {
return;
}
// console.log(data, 'data'); // console.log(data, 'data');
// console.log(props.keyboard, 'props.keyboard'); // console.log(props.keyboard, 'props.keyboard');
// console.log(additionalModules, 'additionalModules()'); // console.log(additionalModules, 'additionalModules()');
@ -261,7 +264,7 @@ const initBpmnModeler = () => {
// propertiesPanel: { // propertiesPanel: {
// parent: '#js-properties-panel' // parent: '#js-properties-panel'
// }, // },
keyboard: props.keyboard ? { bindTo: document } : null, keyboard: props.keyboard ? { bind: true } : null,
// additionalModules: additionalModules.value, // additionalModules: additionalModules.value,
additionalModules: additionalModules.value as any[], additionalModules: additionalModules.value as any[],
moddleExtensions: moddleExtensions.value, moddleExtensions: moddleExtensions.value,

View File

@ -39,10 +39,29 @@ const dialogTitle = ref<string | undefined>(undefined); // 弹窗标题
const selectActivityType = ref<string | undefined>(undefined); // Task const selectActivityType = ref<string | undefined>(undefined); // Task
const selectTasks = ref<any[]>([]); // 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;
/** Zoom恢复 */ /** Zoom恢复 */
const processReZoom = () => { const processReZoom = () => {
defaultZoom.value = 1; defaultZoom.value = 1;
bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto'); getCanvas()?.zoom('fit-viewport', 'auto');
}; };
let resizeObserver: null | ResizeObserver = null; let resizeObserver: null | ResizeObserver = null;
@ -89,7 +108,7 @@ const processZoomIn = (zoomStep = 0.1) => {
); );
} }
defaultZoom.value = newZoom; defaultZoom.value = newZoom;
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value); getCanvas()?.zoom(defaultZoom.value);
}; };
/** Zoom缩小 */ /** Zoom缩小 */
@ -101,7 +120,7 @@ const processZoomOut = (zoomStep = 0.1) => {
); );
} }
defaultZoom.value = newZoom; defaultZoom.value = newZoom;
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value); getCanvas()?.zoom(defaultZoom.value);
}; };
/** 流程图预览清空 */ /** 流程图预览清空 */
@ -122,9 +141,9 @@ const addCustomDefs = () => {
if (!bpmnViewer.value) { if (!bpmnViewer.value) {
return; return;
} }
const canvas = bpmnViewer.value?.get('canvas'); const canvas = getCanvas();
const svg = canvas?._svg; const svg = canvas?._svg;
svg.append(customDefs.value); svg?.append(customDefs.value);
}; };
/** 节点选中 */ /** 节点选中 */
@ -220,8 +239,11 @@ const setProcessStatus = (view: any) => {
finishedSequenceFlowActivityIds, finishedSequenceFlowActivityIds,
rejectedTaskActivityIds, rejectedTaskActivityIds,
} = view; } = view;
const canvas: any = bpmnViewer.value.get('canvas'); const canvas = getCanvas();
const elementRegistry: any = bpmnViewer.value.get('elementRegistry'); const elementRegistry = getElementRegistry();
if (!canvas || !elementRegistry) {
return;
}
// //
if (Array.isArray(finishedSequenceFlowActivityIds)) { if (Array.isArray(finishedSequenceFlowActivityIds)) {
@ -229,7 +251,7 @@ const setProcessStatus = (view: any) => {
if (item !== null) { if (item !== null) {
canvas.addMarker(item, 'success'); canvas.addMarker(item, 'success');
const element = elementRegistry.get(item); const element = elementRegistry.get(item);
const conditionExpression = element.businessObject.conditionExpression; const conditionExpression = element?.businessObject.conditionExpression;
if (conditionExpression) { if (conditionExpression) {
canvas.addMarker(item, 'condition-expression'); canvas.addMarker(item, 'condition-expression');
} }

View File

@ -61,8 +61,8 @@ interface LoopInstanceForm {
} }
const loopInstanceForm = ref<LoopInstanceForm>({}); const loopInstanceForm = ref<LoopInstanceForm>({});
const bpmnElement = ref<any>(null); const bpmnElement = ref<any | null>(null);
const multiLoopInstance = ref<any>(null); const multiLoopInstance = ref<any | null>(null);
declare global { declare global {
interface Window { interface Window {
bpmnInstances?: () => any; bpmnInstances?: () => any;
@ -276,7 +276,7 @@ const approveMethod = ref<ApproveMethodType | undefined>();
const approveRatio = ref<number>(100); const approveRatio = ref<number>(100);
const otherExtensions = ref<any[]>([]); const otherExtensions = ref<any[]>([]);
const getElementLoopNew = (): void => { const getElementLoopNew = (): void => {
if (props.type === 'UserTask') { if (props.type === 'UserTask' && bpmnElement.value) {
const loopCharacteristics = const loopCharacteristics =
bpmnElement.value.businessObject?.loopCharacteristics; bpmnElement.value.businessObject?.loopCharacteristics;
const extensionElements = const extensionElements =
@ -320,6 +320,9 @@ const onApproveRatioChange = (): void => {
updateLoopCharacteristics(); updateLoopCharacteristics();
}; };
const updateLoopCharacteristics = (): void => { const updateLoopCharacteristics = (): void => {
if (!bpmnElement.value) {
return;
}
// ApproveMethodmultiInstanceLoopCharacteristics // ApproveMethodmultiInstanceLoopCharacteristics
if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) { if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
@ -367,9 +370,11 @@ const updateLoopCharacteristics = (): void => {
body: `\${ nrOfCompletedInstances >= nrOfInstances }`, body: `\${ nrOfCompletedInstances >= nrOfInstances }`,
}); });
} }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { if (multiLoopInstance.value) {
loopCharacteristics: toRaw(multiLoopInstance.value), bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
}); loopCharacteristics: toRaw(multiLoopInstance.value),
});
}
} }
// ApproveMethodExtensionElements // ApproveMethodExtensionElements

View File

@ -186,6 +186,12 @@ const multiFormFieldOptions = computed(() => {
(item) => item.type === 'select' || item.type === 'checkbox', (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[]>([]); const childFormFieldOptions = ref<any[]>([]);
/** 保存配置 */ /** 保存配置 */
@ -806,7 +812,7 @@ onMounted(async () => {
}" }"
> >
<InputNumber <InputNumber
v-model:value="configForm.multiInstanceSource" v-model:value="multiInstanceSourceNumber"
:min="1" :min="1"
/> />
</FormItem> </FormItem>

View File

@ -24,19 +24,21 @@ import {
import { getForm } from '#/api/bpm/form'; import { getForm } from '#/api/bpm/form';
import { setConfAndFields2 } from '#/components/form-create'; import { setConfAndFields2 } from '#/components/form-create';
const props = defineProps({ type FormCreateRule = {
formList: { [key: string]: unknown;
type: Array<BpmFormApi.Form>, props?: Record<string, unknown>;
required: true, };
},
}); defineProps<{
formList: BpmFormApi.Form[];
}>();
const formRef = ref(); const formRef = ref();
const modelData = defineModel<any>(); // const modelData = defineModel<any>(); //
const formPreview = ref({ const formPreview = ref({
formData: {} as any, formData: {} as any,
rule: [], rule: [] as FormCreateRule[],
option: { option: {
submitBtn: false, submitBtn: false,
resetBtn: false, resetBtn: false,
@ -63,7 +65,7 @@ watch(
const data = await getForm(newFormId); const data = await getForm(newFormId);
setConfAndFields2(formPreview.value, data.conf, data.fields); setConfAndFields2(formPreview.value, data.conf, data.fields);
// //
formPreview.value.rule.forEach((item: any) => { formPreview.value.rule.forEach((item) => {
item.props = { ...item.props, disabled: true }; item.props = { ...item.props, disabled: true };
}); });
} else { } else {
@ -111,13 +113,12 @@ defineExpose({ validate });
> >
<Select v-model:value="modelData.formId" allow-clear> <Select v-model:value="modelData.formId" allow-clear>
<SelectOption <SelectOption
v-for="form in props.formList" v-for="form in formList"
:key="form.id" :key="form.id"
:value="form.id" :value="form.id"
> >
{{ form.name }} {{ form.name }}
</SelectOption> </SelectOption>
>
</Select> </Select>
</FormItem> </FormItem>
<FormItem <FormItem

View File

@ -28,10 +28,14 @@ const { query } = useRoute();
const formLoading = ref(false); // 12 const formLoading = ref(false); // 12
const processTimeLineLoading = ref(false); // const processTimeLineLoading = ref(false); //
type LeaveCreateData = BpmOALeaveApi.Leave & {
startUserSelectAssignees?: Record<string, number[]>;
};
const processDefineKey = 'oa_leave'; // Key const processDefineKey = 'oa_leave'; // Key
const startUserSelectTasks = ref<any>([]); // const startUserSelectTasks = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); //
const startUserSelectAssignees = ref<any>({}); // const startUserSelectAssignees = ref<Record<string, number[]>>({}); //
const tempStartUserSelectAssignees = ref<any>({}); // const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); //
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); // const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); //
const processDefinitionId = ref(''); const processDefinitionId = ref('');
@ -65,23 +69,21 @@ async function onSubmit() {
// 1.2 // 1.2
if (startUserSelectTasks.value?.length > 0) { if (startUserSelectTasks.value?.length > 0) {
for (const userTask of startUserSelectTasks.value) { for (const userTask of startUserSelectTasks.value) {
if ( const assignees = startUserSelectAssignees.value[userTask.id];
Array.isArray(startUserSelectAssignees.value[userTask.id]) && if (Array.isArray(assignees) && assignees.length === 0) {
startUserSelectAssignees.value[userTask.id].length === 0
) {
return message.warning(`请选择${userTask.name}的审批人`); 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) { if (startUserSelectTasks.value?.length > 0) {
data.startUserSelectAssignees = startUserSelectAssignees.value; data.startUserSelectAssignees = startUserSelectAssignees.value;
} }
// //
const submitData: BpmOALeaveApi.Leave = { const submitData: LeaveCreateData = {
...data, ...data,
startTime: Number(data.startTime), startTime: Number(data.startTime),
endTime: Number(data.endTime), endTime: Number(data.endTime),
@ -144,11 +146,10 @@ async function getApprovalDetail() {
// //
if (startUserSelectTasks.value?.length > 0) { if (startUserSelectTasks.value?.length > 0) {
for (const node of startUserSelectTasks.value) { for (const node of startUserSelectTasks.value) {
startUserSelectAssignees.value[node.id] = const tempAssignees = tempStartUserSelectAssignees.value[node.id];
tempStartUserSelectAssignees.value[node.id] && startUserSelectAssignees.value[node.id] = tempAssignees?.length
tempStartUserSelectAssignees.value[node.id].length > 0 ? tempAssignees
? tempStartUserSelectAssignees.value[node.id] : [];
: [];
} }
} }
} finally { } finally {
@ -157,8 +158,8 @@ async function getApprovalDetail() {
} }
/** 审批相关:选择发起人 */ /** 审批相关:选择发起人 */
function selectUserConfirm(id: string, userList: any[]) { function selectUserConfirm(id: string, userList: Array<{ id: number }>) {
startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id); startUserSelectAssignees.value[id] = userList.map((item) => item.id);
} }
/** 获取请假数据,用于重新发起时自动填充 */ /** 获取请假数据,用于重新发起时自动填充 */

View File

@ -64,8 +64,8 @@ const detailForm = ref<ProcessFormData>({
const fApi = ref<any>(); const fApi = ref<any>();
const startUserSelectTasks = ref<UserTask[]>([]); const startUserSelectTasks = ref<UserTask[]>([]);
const startUserSelectAssignees = ref<Record<string, string[]>>({}); const startUserSelectAssignees = ref<Record<string, number[]>>({});
const tempStartUserSelectAssignees = ref<Record<string, string[]>>({}); const tempStartUserSelectAssignees = ref<Record<string, number[]>>({});
const bpmnXML = ref<string | undefined>(undefined); const bpmnXML = ref<string | undefined>(undefined);
const simpleJson = ref<string | undefined>(undefined); const simpleJson = ref<string | undefined>(undefined);

View File

@ -85,10 +85,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
export function useFormColumns(): VxeTableGridOptions['columns'] { export function useFormColumns(): VxeTableGridOptions['columns'] {
return [ return [
{ {
field: 'defaultStatus', field: 'endStatus',
title: '阶段', title: '阶段',
minWidth: 100, minWidth: 100,
slots: { default: 'defaultStatus' }, slots: { default: 'endStatus' },
}, },
{ {
field: 'name', field: 'name',

View File

@ -108,7 +108,7 @@ async function handleAddStatus() {
formData.value!.statuses!.splice(-3, 0, { formData.value!.statuses!.splice(-3, 0, {
name: '', name: '',
percent: undefined, percent: undefined,
} as any); });
await nextTick(); await nextTick();
await gridApi.grid.reloadData(formData.value!.statuses as any); await gridApi.grid.reloadData(formData.value!.statuses as any);
} }
@ -152,9 +152,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
<Form class="mx-4"> <Form class="mx-4">
<template #statuses> <template #statuses>
<Grid class="w-full"> <Grid class="w-full">
<template #defaultStatus="{ row, rowIndex }"> <template #endStatus="{ row, rowIndex }">
<span> <span>
{{ row.defaultStatus ? '结束' : `阶段${rowIndex + 1}` }} {{ row.endStatus ? '结束' : `阶段${rowIndex + 1}` }}
</span> </span>
</template> </template>
<template #name="{ row }"> <template #name="{ row }">

View File

@ -1,4 +1,4 @@
import { ref, type VNodeRef, watch } from 'vue' import { type VNodeRef, watch } from 'vue'
/** /**
* MediaStream `<video>` / `<audio>` srcObject * MediaStream `<video>` / `<audio>` srcObject
@ -7,21 +7,34 @@ import { ref, type VNodeRef, watch } from 'vue'
export function useMediaStreamElement<T extends HTMLMediaElement>( export function useMediaStreamElement<T extends HTMLMediaElement>(
streamSource: () => MediaStream | null | undefined streamSource: () => MediaStream | null | undefined
): VNodeRef { ): VNodeRef {
const elRef = ref<T>() let el: T | null = null
const syncStream = (stream = streamSource()) => { let currentStream: MediaStream | null | undefined
if (elRef.value) {
elRef.value.srcObject = stream || null const syncStream = () => {
if (el) {
el.srcObject = currentStream || null
} }
} }
watch( watch(
streamSource, streamSource,
(stream) => { (stream) => {
syncStream(stream) currentStream = stream
syncStream()
}, },
{ flush: 'post', immediate: true } { flush: 'post', immediate: true }
) )
return (el) => {
elRef.value = el instanceof HTMLMediaElement ? (el as T) : undefined return (value) => {
syncStream() if (value instanceof HTMLMediaElement) {
el = value as T
syncStream()
return
}
if (el) {
el.srcObject = null
}
el = null
} }
} }

View File

@ -78,7 +78,7 @@ const [Grid] = useVbenVxeGrid({
<template #atUsers="{ row }"> <template #atUsers="{ row }">
<template v-if="row.atUserIds?.length"> <template v-if="row.atUserIds?.length">
<span v-for="(userId, index) in row.atUserIds" :key="userId"> <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-if="userId === IM_AT_ALL_USER_ID">@{{ IM_AT_ALL_NICKNAME }}</template>
<template v-else>@{{ row.atUserNicknames?.[index] || userId }}</template> <template v-else>@{{ row.atUserNicknames?.[index] || userId }}</template>
</span> </span>

View File

@ -62,7 +62,7 @@ defineExpose({ open });
<DescriptionsItem label="@用户" :span="2"> <DescriptionsItem label="@用户" :span="2">
<template v-if="detail.atUserIds?.length"> <template v-if="detail.atUserIds?.length">
<span v-for="(userId, index) in detail.atUserIds" :key="userId"> <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"> <template v-if="userId === IM_AT_ALL_USER_ID">
@{{ IM_AT_ALL_NICKNAME }} @{{ IM_AT_ALL_NICKNAME }}
</template> </template>

View File

@ -17,7 +17,7 @@ import {
const props = defineProps<{ const props = defineProps<{
columns?: InfraCodegenApi.CodegenColumn[]; columns?: InfraCodegenApi.CodegenColumn[];
table?: InfraCodegenApi.CodegenTable; table?: InfraCodegenApi.CodegenTableSaveReqVO;
}>(); }>();
const tables = ref<InfraCodegenApi.CodegenTable[]>([]); const tables = ref<InfraCodegenApi.CodegenTable[]>([]);
@ -70,7 +70,7 @@ function updateTreeSchema(): void {
schema: useGenerationInfoTreeFormSchema(props.columns), schema: useGenerationInfoTreeFormSchema(props.columns),
}); });
// //
treeFormApi.setValues(props.table as any); treeFormApi.setValues(props.table || {});
} }
/** 更新主子表信息表单 schema */ /** 更新主子表信息表单 schema */
@ -79,7 +79,7 @@ function updateSubSchema(): void {
schema: useGenerationInfoSubTableFormSchema(props.columns, tables.value), schema: useGenerationInfoSubTableFormSchema(props.columns, tables.value),
}); });
// //
subFormApi.setValues(props.table as any); subFormApi.setValues(props.table || {});
} }
/** 获取合并的表单值 */ /** 获取合并的表单值 */
@ -140,7 +140,7 @@ watch(
return; return;
} }
const table = val as InfraCodegenApi.CodegenTable; const table = val;
// schema // schema
updateTreeSchema(); updateTreeSchema();
// //

View File

@ -148,7 +148,7 @@ function handleRowCheckboxChange({
}: { }: {
records: Demo03StudentApi.Demo03Student[]; records: Demo03StudentApi.Demo03Student[];
}) { }) {
checkedIds.value = records.map((item) => item.id)!; checkedIds.value = records.map((item) => item.id!);
} }
/** 导出表格 */ /** 导出表格 */

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form'; import type { Rule } from 'ant-design-vue/es/form';
import type { Dayjs } from 'dayjs';
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp'; import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
@ -28,8 +29,15 @@ import { $t } from '#/locales';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
type Demo03StudentFormData = Omit<
Demo03StudentApi.Demo03Student,
'birthday'
> & {
birthday?: Dayjs | string;
};
const formRef = ref(); const formRef = ref();
const formData = ref<Partial<Demo03StudentApi.Demo03Student>>({ const formData = ref<Partial<Demo03StudentFormData>>({
id: undefined, id: undefined,
name: undefined, name: undefined,
sex: undefined, sex: undefined,
@ -96,7 +104,10 @@ const [Modal, modalApi] = useVbenModal({
modalApi.unlock(); modalApi.unlock();
} }
} }
formData.value = data; formData.value = {
...data,
birthday: data.birthday === undefined ? undefined : String(data.birthday),
};
}, },
}); });
</script> </script>

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form'; import type { Rule } from 'ant-design-vue/es/form';
import type { Dayjs } from 'dayjs';
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal'; import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
@ -32,8 +33,15 @@ import Demo03GradeForm from './demo03-grade-form.vue';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
type Demo03StudentFormData = Omit<
Demo03StudentApi.Demo03Student,
'birthday'
> & {
birthday?: Dayjs | string;
};
const formRef = ref(); const formRef = ref();
const formData = ref<Partial<Demo03StudentApi.Demo03Student>>({ const formData = ref<Partial<Demo03StudentFormData>>({
id: undefined, id: undefined,
name: undefined, name: undefined,
sex: undefined, sex: undefined,
@ -116,7 +124,10 @@ const [Modal, modalApi] = useVbenModal({
modalApi.unlock(); modalApi.unlock();
} }
} }
formData.value = data; formData.value = {
...data,
birthday: data.birthday === undefined ? undefined : String(data.birthday),
};
}, },
}); });
</script> </script>

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form'; import type { Rule } from 'ant-design-vue/es/form';
import type { Dayjs } from 'dayjs';
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal'; import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
@ -32,8 +33,15 @@ import Demo03GradeForm from './demo03-grade-form.vue';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
type Demo03StudentFormData = Omit<
Demo03StudentApi.Demo03Student,
'birthday'
> & {
birthday?: Dayjs | string;
};
const formRef = ref(); const formRef = ref();
const formData = ref<Partial<Demo03StudentApi.Demo03Student>>({ const formData = ref<Partial<Demo03StudentFormData>>({
id: undefined, id: undefined,
name: undefined, name: undefined,
sex: undefined, sex: undefined,
@ -115,7 +123,10 @@ const [Modal, modalApi] = useVbenModal({
modalApi.unlock(); modalApi.unlock();
} }
} }
formData.value = data; formData.value = {
...data,
birthday: data.birthday === undefined ? undefined : String(data.birthday),
};
}, },
}); });
</script> </script>

View File

@ -76,7 +76,7 @@ const isDeviceCondition = computed(() => {
* @param value 字段值 * @param value 字段值
*/ */
function updateConditionField(field: any, value: any) { function updateConditionField(field: any, value: any) {
(condition.value as any)[field] = value; Object.assign(condition.value, { [field]: value });
emit('update:modelValue', condition.value); emit('update:modelValue', condition.value);
} }

View File

@ -122,7 +122,7 @@ const timeValue2 = computed(() => {
* @param value 字段值 * @param value 字段值
*/ */
function updateConditionField(field: any, value: any) { function updateConditionField(field: any, value: any) {
(condition.value as any)[field] = value; Object.assign(condition.value, { [field]: value });
} }
/** /**

View File

@ -111,7 +111,7 @@ const serviceConfig = computed(() => {
* @param value 字段值 * @param value 字段值
*/ */
function updateConditionField(field: any, value: any) { function updateConditionField(field: any, value: any) {
(condition.value as any)[field] = value; Object.assign(condition.value, { [field]: value });
} }
/** /**

View File

@ -166,7 +166,7 @@ if (!props.isStructDataSpecs && !props.isParams) {
label="数据长度" label="数据长度"
> >
<Input <Input
v-model:value="property.dataSpecs.length" v-model:value="property.dataSpecs!.length"
class="!w-[255px]" class="!w-[255px]"
placeholder="请输入文本字节长度" placeholder="请输入文本字节长度"
> >

View File

@ -19,6 +19,10 @@ const expanded = ref(false); // 是否展开
function handleToggleFab() { function handleToggleFab() {
expanded.value = !expanded.value; expanded.value = !expanded.value;
} }
function handleActive() {
expanded.value = false;
}
</script> </script>
<template> <template>
<div <div
@ -35,6 +39,7 @@ function handleToggleFab() {
v-for="(item, index) in property.list" v-for="(item, index) in property.list"
:key="index" :key="index"
class="flex flex-col items-center" class="flex flex-col items-center"
@click="handleActive"
> >
<Image :src="item.imgUrl" :width="28" :height="28" :preview="false"> <Image :src="item.imgUrl" :width="28" :height="28" :preview="false">
<template #error> <template #error>

View File

@ -21,7 +21,9 @@ const spuList = ref<MallSpuApi.Spu[]>([]);
watch( watch(
() => props.property.spuIds, () => props.property.spuIds,
async () => { async () => {
spuList.value = await getSpuDetailList(props.property.spuIds); spuList.value = props.property.spuIds.length > 0
? await getSpuDetailList(props.property.spuIds)
: [];
}, },
{ {
immediate: true, immediate: true,

View File

@ -47,7 +47,7 @@ export function totalCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
if (row.totalCount === -1) { if (row.totalCount === -1) {
return '不限制'; return '不限制';
} }
return row.totalCount; return `${row.totalCount}`;
} }
/** 格式化【剩余数量】 */ /** 格式化【剩余数量】 */
@ -55,7 +55,7 @@ export function remainedCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
if (row.totalCount === -1) { if (row.totalCount === -1) {
return '不限制'; return '不限制';
} }
return row.totalCount - row.takeCount; return `${row.totalCount - row.takeCount}`;
} }
/** 格式化【最低消费】 */ /** 格式化【最低消费】 */

View File

@ -205,14 +205,7 @@ export function useFormSchema(): VbenFormSchema[] {
values.productScope === PromotionProductScopeEnum.CATEGORY.scope && values.productScope === PromotionProductScopeEnum.CATEGORY.scope &&
values.productScopeValues values.productScopeValues
) { ) {
const categoryIds = values.productScopeValues; form.setFieldValue('productCategoryIds', values.productScopeValues);
// 单选时使用数组不能反显,取第一个元素
form.setFieldValue(
'productCategoryIds',
Array.isArray(categoryIds) && categoryIds.length > 0
? categoryIds[0]
: categoryIds,
);
} }
}, },
}, },
@ -237,9 +230,12 @@ export function useFormSchema(): VbenFormSchema[] {
trigger(values, form) { trigger(values, form) {
switch (values.productScope) { switch (values.productScope) {
case PromotionProductScopeEnum.CATEGORY.scope: { case PromotionProductScopeEnum.CATEGORY.scope: {
const categoryIds = Array.isArray(values.productCategoryIds) let categoryIds: number[] = [];
? values.productCategoryIds if (Array.isArray(values.productCategoryIds)) {
: [values.productCategoryIds]; categoryIds = values.productCategoryIds;
} else if (values.productCategoryIds) {
categoryIds = [values.productCategoryIds];
}
form.setFieldValue('productScopeValues', categoryIds); form.setFieldValue('productScopeValues', categoryIds);
break; break;
} }

View File

@ -27,11 +27,18 @@ import RewardRule from './reward-rule.vue';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formData = ref<Partial<MallRewardActivityApi.RewardActivity>>({ const createDefaultFormData = (): Partial<MallRewardActivityApi.RewardActivity> => ({
conditionType: PromotionConditionTypeEnum.PRICE.type, conditionType: PromotionConditionTypeEnum.PRICE.type,
productScope: PromotionProductScopeEnum.ALL.scope, productScope: PromotionProductScopeEnum.ALL.scope,
productScopeValues: [],
productCategoryIds: [],
productSpuIds: [],
rules: [], rules: [],
}); });
const formData = ref<Partial<MallRewardActivityApi.RewardActivity>>(
createDefaultFormData(),
);
const getTitle = computed(() => { const getTitle = computed(() => {
return formData.value?.id return formData.value?.id
? $t('ui.actionTitle.edit', ['满减送']) ? $t('ui.actionTitle.edit', ['满减送'])
@ -106,17 +113,22 @@ const [Modal, modalApi] = useVbenModal({
}, },
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
formData.value = {}; formData.value = createDefaultFormData();
return; return;
} }
// //
const data = modalApi.getData<MallRewardActivityApi.RewardActivity>(); const data = modalApi.getData<MallRewardActivityApi.RewardActivity>();
if (!data || !data.id) { if (!data || !data.id) {
formData.value = createDefaultFormData();
await formApi.setValues(formData.value);
return; return;
} }
modalApi.lock(); modalApi.lock();
try { try {
const result = await getReward(data.id); const result = {
...createDefaultFormData(),
...(await getReward(data.id)),
};
result.startAndEndTime = [ result.startAndEndTime = [
result.startTime ? String(result.startTime) : undefined, result.startTime ? String(result.startTime) : undefined,
result.endTime ? String(result.endTime) : undefined, result.endTime ? String(result.endTime) : undefined,

View File

@ -63,7 +63,10 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock(); modalApi.lock();
try { try {
// values // values
await formApi.setValues(data); await formApi.setValues({
id: data.id,
remark: data.remark || '',
});
} finally { } finally {
modalApi.unlock(); modalApi.unlock();
} }

View File

@ -17,7 +17,7 @@ import { useBalanceFormSchema } from '../data';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formData = ref({ const formData = ref({
id: 0, id: undefined as number | undefined,
nickname: '', nickname: '',
balance: '0', balance: '0',
changeBalance: 0, changeBalance: 0,
@ -48,7 +48,7 @@ const [Modal, modalApi] = useVbenModal({
const data = await formApi.getValues(); const data = await formApi.getValues();
try { try {
await updateWalletBalance({ await updateWalletBalance({
userId: data.id, userId: data.id!,
balance: yuanToFen(data.changeBalance) * data.changeType, balance: yuanToFen(data.changeBalance) * data.changeType,
}); });
// //

View File

@ -20,7 +20,7 @@
"build:analyze": "pnpm vite build --mode analyze", "build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development", "dev": "pnpm vite --mode development",
"preview": "vite preview", "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": { "imports": {
"#/*": "./src/*" "#/*": "./src/*"

View File

@ -740,22 +740,18 @@ async function initComponentAdapter() {
Rate, Rate,
RichEditor: withDefaultPlaceholder(VbenTiptap, 'input', { RichEditor: withDefaultPlaceholder(VbenTiptap, 'input', {
imageUpload: { imageUpload: {
upload: (file: any, onProgress: any) => { upload: async (file: File, onProgress?: (percent: number) => void) => {
return new Promise((resolve, reject) => { try {
uploadFileApi({ const response = await uploadFileApi({ file }, (progressEvent) => {
file, const percent = progressEvent.total
onProgress({ percent }) { ? Math.round((progressEvent.loaded * 100) / progressEvent.total)
onProgress?.(percent); : 0;
}, onProgress?.(percent);
onSuccess(response) {
// 从响应中提取图片URL
resolve(response?.data?.url ?? response?.url ?? '');
},
onError() {
reject(new Error($t('ui.tiptap.upload.uploadFailed')));
},
}); });
}); return response?.data?.url ?? response?.url ?? '';
} catch {
throw new Error($t('ui.tiptap.upload.uploadFailed'));
}
}, },
}, },
}), }),

View File

@ -14,7 +14,7 @@ export namespace AiChatConversationApi {
temperature: number; // 温度参数 temperature: number; // 温度参数
maxTokens: number; // 单条回复的最大 Token 数量 maxTokens: number; // 单条回复的最大 Token 数量
maxContexts: number; // 上下文的最大 Message 数量 maxContexts: number; // 上下文的最大 Message 数量
createTime?: Date; // 创建时间 createTime: Date; // 创建时间
systemMessage?: string; // 角色设定 systemMessage?: string; // 角色设定
modelName?: string; // 模型名字 modelName?: string; // 模型名字
roleAvatar?: string; // 角色头像 roleAvatar?: string; // 角色头像

View File

@ -34,15 +34,15 @@ export namespace AiImageApi {
prompt: string; // 提示词 prompt: string; // 提示词
modelId: number; // 模型 modelId: number; // 模型
style: string; // 图像生成的风格 style: string; // 图像生成的风格
width: string; // 图片宽度 width: number; // 图片宽度
height: string; // 图片高度 height: number; // 图片高度
options: object; // 绘制参数Map<String, String> options: object; // 绘制参数Map<String, String>
} }
export interface ImageMidjourneyImagineReqVO { export interface ImageMidjourneyImagineReqVO {
prompt: string; // 提示词 prompt: string; // 提示词
modelId: number; // 模型 modelId: number; // 模型
base64Array?: string[]; // size不能为空 base64Array: string[]; // 参考图 base64 列表
width: string; // 图片宽度 width: string; // 图片宽度
height: string; // 图片高度 height: string; // 图片高度
version: string; // 版本 version: string; // 版本

View File

@ -12,7 +12,6 @@ export namespace BpmOALeaveApi {
startTime: number; startTime: number;
endTime: number; endTime: number;
createTime: Date; createTime: Date;
startUserSelectAssignees?: Record<string, string[]>;
} }
} }

View File

@ -84,7 +84,6 @@ export namespace BpmProcessInstanceApi {
reason: string; reason: string;
signPicUrl: string; signPicUrl: string;
status: number; status: number;
attachments?: string[];
} }
/** 抄送流程实例 */ /** 抄送流程实例 */

View File

@ -11,15 +11,16 @@ export namespace CrmBusinessStatusApi {
deptNames?: string[]; deptNames?: string[];
creator?: string; creator?: string;
createTime?: Date; createTime?: Date;
statuses?: BusinessStatusType[]; statuses: BusinessStatusType[];
} }
/** 商机状态信息 */ /** 商机状态信息 */
export interface BusinessStatusType { export interface BusinessStatusType {
id?: number; id?: number;
name: string; name: string;
percent: number; percent?: number;
[x: string]: any; endStatus?: number;
key?: string;
} }
} }
@ -43,7 +44,7 @@ export const DEFAULT_STATUSES = [
name: '无效', name: '无效',
percent: 0, percent: 0,
}, },
]; ] satisfies CrmBusinessStatusApi.BusinessStatusType[];
/** 查询商机状态组列表 */ /** 查询商机状态组列表 */
export function getBusinessStatusPage(params: PageParam) { export function getBusinessStatusPage(params: PageParam) {

View File

@ -24,6 +24,18 @@ export namespace InfraCodegenApi {
parentMenuId: number; 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 { export interface CodegenColumn {
id: number; id: number;
@ -54,7 +66,7 @@ export namespace InfraCodegenApi {
/** 代码生成详情 */ /** 代码生成详情 */
export interface CodegenDetail { export interface CodegenDetail {
table: CodegenTable; table: CodegenTableSaveReqVO;
columns: CodegenColumn[]; columns: CodegenColumn[];
} }
@ -66,7 +78,7 @@ export namespace InfraCodegenApi {
/** 更新代码生成请求 */ /** 更新代码生成请求 */
export interface CodegenUpdateReqVO { export interface CodegenUpdateReqVO {
table: any | CodegenTable; table: CodegenTableSaveReqVO;
columns: CodegenColumn[]; columns: CodegenColumn[];
} }

View File

@ -11,7 +11,7 @@ export namespace Demo02CategoryApi {
} }
/** 查询示例分类列表 */ /** 查询示例分类列表 */
export function getDemo02CategoryList(params: any) { export function getDemo02CategoryList(params?: any) {
return requestClient.get<Demo02CategoryApi.Demo02Category[]>( return requestClient.get<Demo02CategoryApi.Demo02Category[]>(
'/infra/demo02-category/list', '/infra/demo02-category/list',
{ params }, { params },

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
} }
} }

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
demo03courses?: Demo03Course[]; demo03courses?: Demo03Course[];
demo03grade?: Demo03Grade; demo03grade?: Demo03Grade;

View File

@ -1,5 +1,3 @@
import type { Dayjs } from 'dayjs';
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@ -7,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace Demo03StudentApi { export namespace Demo03StudentApi {
/** 学生课程信息 */ /** 学生课程信息 */
export interface Demo03Course { export interface Demo03Course {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
score?: number; // 分数 score?: number; // 分数
@ -15,7 +13,7 @@ export namespace Demo03StudentApi {
/** 学生班级信息 */ /** 学生班级信息 */
export interface Demo03Grade { export interface Demo03Grade {
id: number; // 编号 id?: number; // 编号
studentId?: number; // 学生编号 studentId?: number; // 学生编号
name?: string; // 名字 name?: string; // 名字
teacher?: string; // 班主任 teacher?: string; // 班主任
@ -23,10 +21,10 @@ export namespace Demo03StudentApi {
/** 学生信息 */ /** 学生信息 */
export interface Demo03Student { export interface Demo03Student {
id: number; // 编号 id?: number; // 编号
name?: string; // 名字 name?: string; // 名字
sex?: number; // 性别 sex?: number; // 性别
birthday?: Dayjs | string; // 出生日期 birthday?: number | string; // 出生日期
description?: string; // 简介 description?: string; // 简介
demo03courses?: Demo03Course[]; demo03courses?: Demo03Course[];
demo03grade?: Demo03Grade; demo03grade?: Demo03Grade;

View File

@ -6,11 +6,11 @@ export namespace IoTOtaTaskApi {
/** IoT OTA 升级任务 */ /** IoT OTA 升级任务 */
export interface Task { export interface Task {
id?: number; id?: number;
name?: string; name: string;
description?: string; description?: string;
firmwareId?: number; firmwareId?: number;
status?: number; status?: number;
deviceScope?: number; deviceScope: number;
deviceIds?: number[]; deviceIds?: number[];
deviceTotalCount?: number; deviceTotalCount?: number;
deviceSuccessCount?: number; deviceSuccessCount?: number;

View File

@ -32,8 +32,8 @@ export namespace ThingModelApi {
required?: boolean; required?: boolean;
dataType?: string; dataType?: string;
description?: string; description?: string;
dataSpecs?: any; dataSpecs?: ThingModelDataSpecs;
dataSpecsList?: any[]; dataSpecsList?: ThingModelPropertyDataSpecs[];
} }
/** IoT 物模型服务 */ /** IoT 物模型服务 */
@ -66,8 +66,8 @@ export namespace ThingModelApi {
direction?: string; direction?: string;
paraOrder?: number; paraOrder?: number;
dataType?: string; dataType?: string;
dataSpecs?: any; dataSpecs?: ThingModelDataSpecs;
dataSpecsList?: any[]; dataSpecsList?: ThingModelPropertyDataSpecs[];
} }
/** IoT 物模型 TSL树形响应 */ /** IoT 物模型 TSL树形响应 */
@ -80,19 +80,35 @@ export namespace ThingModelApi {
} }
/** IoT 数据定义(数值型) */ /** 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; min?: number | string;
max?: number | string; max?: number | string;
name?: string;
precise?: string;
required?: boolean;
size?: number | string;
step?: number | string; step?: number | string;
unit?: string; unit?: string;
unitName?: string; unitName?: string;
value?: number | string;
} }
/** IoT 数据定义(数值型) */
export type DataSpecsNumberData = ThingModelDataSpecs;
/** IoT 数据定义(枚举/布尔型) */ /** IoT 数据定义(枚举/布尔型) */
export interface DataSpecsEnumOrBoolData { export type DataSpecsEnumOrBoolData = ThingModelDataSpecs;
value: number | string;
name: string; export type ThingModelPropertyDataSpecs = Property & ThingModelDataSpecs;
}
} }
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */

View File

@ -8,6 +8,7 @@ export namespace MallCombinationActivityApi {
id?: number; // 活动编号 id?: number; // 活动编号
name?: string; // 活动名称 name?: string; // 活动名称
spuId?: number; // 商品 SPU 编号 spuId?: number; // 商品 SPU 编号
spuName?: string; // 商品 SPU 名称
totalLimitCount?: number; // 总限购数量 totalLimitCount?: number; // 总限购数量
singleLimitCount?: number; // 单次限购数量 singleLimitCount?: number; // 单次限购数量
startTime?: Date; // 开始时间 startTime?: Date; // 开始时间
@ -21,7 +22,7 @@ export namespace MallCombinationActivityApi {
limitDuration?: number; // 限制时长 limitDuration?: number; // 限制时长
combinationPrice?: number; // 拼团价格 combinationPrice?: number; // 拼团价格
products: CombinationProduct[]; // 商品列表 products: CombinationProduct[]; // 商品列表
picUrl?: any; picUrl?: string; // 商品图片
} }
/** 拼团活动所需属性 */ /** 拼团活动所需属性 */

View File

@ -26,9 +26,9 @@ export namespace MallRewardActivityApi {
conditionType?: number; // 条件类型 conditionType?: number; // 条件类型
productScope?: number; // 商品范围 productScope?: number; // 商品范围
rules: RewardRule[]; // 优惠规则列表 rules: RewardRule[]; // 优惠规则列表
productScopeValues?: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表 productScopeValues: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表
productCategoryIds?: number[]; // 商品分类编号列表(仅表单使用) productCategoryIds: number[]; // 商品分类编号列表(仅表单使用)
productSpuIds?: number[]; // 商品 SPU 编号列表(仅表单使用) productSpuIds: number[]; // 商品 SPU 编号列表(仅表单使用)
} }
} }

View File

@ -31,7 +31,7 @@ export namespace MallSeckillActivityApi {
totalStock?: number; // 秒杀总库存 totalStock?: number; // 秒杀总库存
seckillPrice?: number; // 秒杀价格 seckillPrice?: number; // 秒杀价格
products?: SeckillProduct[]; // 秒杀商品列表 products?: SeckillProduct[]; // 秒杀商品列表
picUrl?: any; picUrl?: string; // 商品图片
} }
} }

View File

@ -4,20 +4,21 @@ export namespace MallTradeConfigApi {
/** 交易中心配置 */ /** 交易中心配置 */
export interface Config { export interface Config {
id?: number; id?: number;
afterSaleRefundReasons?: string[]; afterSaleRefundReasons: string[];
afterSaleReturnReasons?: string[]; afterSaleReturnReasons: string[];
deliveryExpressFreeEnabled?: boolean; deliveryExpressFreeEnabled: boolean;
deliveryExpressFreePrice?: number; deliveryExpressFreePrice: number;
deliveryPickUpEnabled?: boolean; deliveryPickUpEnabled: boolean;
brokerageEnabled?: boolean; brokerageEnabled?: boolean;
brokerageEnabledCondition?: number; brokerageEnabledCondition?: number;
brokerageBindMode?: number; brokerageBindMode?: number;
brokeragePosterUrls?: string; brokeragePosterUrls: string[];
brokerageFirstPercent?: number; brokerageFirstPercent?: number;
brokerageSecondPercent?: number; brokerageSecondPercent?: number;
brokerageWithdrawMinPrice?: number; brokerageWithdrawMinPrice: number;
brokerageFrozenDays?: number; brokerageFrozenDays: number;
brokerageWithdrawTypes?: string; brokerageWithdrawFeePercent: number;
brokerageWithdrawTypes: number[];
tencentLbsKey?: string; tencentLbsKey?: string;
} }
} }

View File

@ -4,7 +4,7 @@ export namespace MemberConfigApi {
/** 积分设置信息 */ /** 积分设置信息 */
export interface Config { export interface Config {
id?: number; id?: number;
pointTradeDeductEnable: number; pointTradeDeductEnable: boolean;
pointTradeDeductUnitPrice: number; pointTradeDeductUnitPrice: number;
pointTradeDeductMaxPrice: number; pointTradeDeductMaxPrice: number;
pointTradeGivePoint: number; pointTradeGivePoint: number;

View File

@ -10,27 +10,31 @@ export namespace MemberUserApi {
birthday?: number; birthday?: number;
createTime?: number; createTime?: number;
loginDate?: number; loginDate?: number;
loginIp: string; loginIp?: string;
mark: string; mark?: string;
mobile: string; mobile?: string;
email?: string; email?: string;
name?: string; name?: string;
nickname?: string; nickname?: string;
registerIp: string; registerIp?: string;
sex: number; sex?: number;
status: number; status?: number;
areaId?: number; areaId?: number;
areaName?: string; areaName?: string;
levelName: string; tagIds?: number[];
point?: number; groupId?: number;
totalPoint?: number; levelId?: number;
experience?: number; levelName?: null | string;
point?: null | number;
totalPoint?: null | number;
experience?: null | number;
} }
/** 会员用户等级更新信息 */ /** 会员用户等级更新信息 */
export interface UserUpdateLevelReqVO { export interface UserUpdateLevelReqVO {
id: number; id: number;
levelId: number; levelId: number;
reason: string;
} }
/** 会员用户积分更新信息 */ /** 会员用户积分更新信息 */

View File

@ -11,6 +11,12 @@ export namespace MpTagApi {
count?: number; count?: number;
createTime?: Date; createTime?: Date;
} }
/** 标签精简信息 */
export interface SimpleTag {
tagId: number;
name: string;
}
} }
/** 创建公众号标签 */ /** 创建公众号标签 */
@ -46,7 +52,7 @@ export function getTagPage(params: PageParam) {
/** 获取公众号标签精简信息列表 */ /** 获取公众号标签精简信息列表 */
export function getSimpleTagList() { export function getSimpleTagList() {
return requestClient.get<MpTagApi.Tag[]>('/mp/tag/list-all-simple'); return requestClient.get<MpTagApi.SimpleTag[]>('/mp/tag/list-all-simple');
} }
/** 同步公众号标签 */ /** 同步公众号标签 */

View File

@ -3,6 +3,16 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace PayNotifyApi { export namespace PayNotifyApi {
/** 支付通知日志 */
export interface NotifyLog {
id?: number;
status?: number;
notifyTimes?: number;
lastExecuteTime?: Date;
createTime?: Date;
response?: string;
}
/** 支付通知任务 */ /** 支付通知任务 */
export interface NotifyTask { export interface NotifyTask {
id: number; id: number;
@ -20,13 +30,13 @@ export namespace PayNotifyApi {
maxNotifyTimes: number; maxNotifyTimes: number;
createTime: Date; createTime: Date;
updateTime: Date; updateTime: Date;
logs?: any[]; logs?: NotifyLog[];
} }
} }
/** 获得支付通知明细 */ /** 获得支付通知明细 */
export function getNotifyTaskDetail(id: number) { 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}`);
} }
/** 获得支付通知分页 */ /** 获得支付通知分页 */

View File

@ -21,7 +21,6 @@ export function useDescription(options?: Partial<DescriptionProps>) {
inheritAttrs: false, inheritAttrs: false,
setup(_props, { attrs, slots }) { setup(_props, { attrs, slots }) {
return () => { return () => {
// @ts-expect-error - 避免类型实例化过深
return h(Description, { ...propsState, ...attrs }, slots); return h(Description, { ...propsState, ...attrs }, slots);
}; };
}, },

View File

@ -10,7 +10,7 @@ export interface PopConfirm {
disabled?: boolean; disabled?: boolean;
} }
export interface ActionItem extends ButtonProps { export interface ActionItem extends Omit<ButtonProps, 'color'> {
onClick?: () => void; onClick?: () => void;
type?: ButtonType; type?: ButtonType;
label?: string; label?: string;

View File

@ -33,7 +33,7 @@ const getButtonProps = computed(() => {
}; };
}); });
async function customRequest(info: UploadRequestOption<any>) { async function customRequest(info: UploadRequestOption) {
// 1. emit // 1. emit
const file = info.file as File; const file = info.file as File;
const name = file?.name; const name = file?.name;

View File

@ -90,7 +90,7 @@ export const useMallKefuStore = defineStore('mall-kefu', {
}, },
conversationSort() { conversationSort() {
// 按置顶属性和最后消息时间排序 // 按置顶属性和最后消息时间排序
this.conversationList.toSorted((a, b) => { this.conversationList = this.conversationList.toSorted((a, b) => {
// 按照置顶排序,置顶的会在前面 // 按照置顶排序,置顶的会在前面
if (a.adminPinned !== b.adminPinned) { if (a.adminPinned !== b.adminPinned) {
return a.adminPinned ? -1 : 1; return a.adminPinned ? -1 : 1;

View File

@ -90,7 +90,7 @@ async function getChatConversationList() {
// 1.1 // 1.1
conversationList.value = await getChatConversationMyList(); conversationList.value = await getChatConversationMyList();
// 1.2 // 1.2
conversationList.value.toSorted((a, b) => { conversationList.value = conversationList.value.toSorted((a, b) => {
return Number(b.createTime) - Number(a.createTime); return Number(b.createTime) - Number(a.createTime);
}); });
// 1.3 // 1.3

View File

@ -20,8 +20,8 @@ const props = defineProps({
const emits = defineEmits(['onBtnClick', 'onMjBtnClick']); const emits = defineEmits(['onBtnClick', 'onMjBtnClick']);
/** 处理点击事件 */ /** 处理点击事件 */
async function handleButtonClick(type: string, detail: AiImageApi.Image) { async function handleButtonClick(type: string) {
emits('onBtnClick', type, detail); emits('onBtnClick', type, props.detail);
} }
/** 处理 Midjourney 按钮点击事件 */ /** 处理 Midjourney 按钮点击事件 */
@ -79,28 +79,28 @@ onMounted(async () => {
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('download', detail)" @click="handleButtonClick('download')"
> >
<IconifyIcon icon="lucide:download" /> <IconifyIcon icon="lucide:download" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('regeneration', detail)" @click="handleButtonClick('regeneration')"
> >
<IconifyIcon icon="lucide:refresh-cw" /> <IconifyIcon icon="lucide:refresh-cw" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('delete', detail)" @click="handleButtonClick('delete')"
> >
<IconifyIcon icon="lucide:trash" /> <IconifyIcon icon="lucide:trash" />
</Button> </Button>
<Button <Button
class="m-0 p-2" class="m-0 p-2"
type="text" type="text"
@click="handleButtonClick('more', detail)" @click="handleButtonClick('more')"
> >
<IconifyIcon icon="lucide:ellipsis-vertical" /> <IconifyIcon icon="lucide:ellipsis-vertical" />
</Button> </Button>
@ -119,8 +119,8 @@ onMounted(async () => {
<div class="mt-2 flex w-full flex-wrap justify-start"> <div class="mt-2 flex w-full flex-wrap justify-start">
<Button <Button
size="small" size="small"
v-for="(button, index) in detail?.buttons" v-for="button in detail?.buttons"
:key="index" :key="button.customId"
class="m-2 ml-0 min-w-10" class="m-2 ml-0 min-w-10"
@click="handleMidjourneyBtnClick(button)" @click="handleMidjourneyBtnClick(button)"
> >

View File

@ -111,8 +111,8 @@ async function handleGenerateImage() {
prompt: prompt.value, // prompt: prompt.value, //
modelId: matchedModel.id, // 使 modelId: matchedModel.id, // 使
style: style.value, // style: style.value, //
width: imageSize.width, // size width: Number(imageSize.width), // size
height: imageSize.height, // size height: Number(imageSize.height), // size
options: { options: {
style: style.value, // style: style.value, //
}, },

View File

@ -92,6 +92,7 @@ async function handleGenerateImage() {
const req = { const req = {
prompt: prompt.value, prompt: prompt.value,
modelId: matchedModel.id, modelId: matchedModel.id,
base64Array: [],
width: imageSize.width, width: imageSize.width,
height: imageSize.height, height: imageSize.height,
version: selectVersion.value, version: selectVersion.value,

View File

@ -1,16 +1,22 @@
<script lang="ts" setup> <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 { IconifyIcon } from '@vben/icons';
import { formatPast } from '@vben/utils';
import { Image, Slider } from 'antdv-next'; import { Image, Slider } from 'antdv-next';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicAudioBarIndex' }); 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 audioRef = ref<HTMLAudioElement | null>(null);
const audioProgress = ref(0);
const audioDuration = ref(0);
const audioProps = reactive<any>({ const audioProps = reactive<any>({
autoplay: true, autoplay: true,
paused: false, paused: false,
@ -20,6 +26,17 @@ const audioProps = reactive<any>({
volume: 50, volume: 50,
}); // https://www.runoob.com/tags/ref-av-dom.html }); // 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) { function toggleStatus(type: string) {
audioProps[type] = !audioProps[type]; audioProps[type] = !audioProps[type];
if (type === 'paused' && audioRef.value) { if (type === 'paused' && audioRef.value) {
@ -32,9 +49,40 @@ function toggleStatus(type: string) {
} }
/** 更新播放位置 */ /** 更新播放位置 */
function audioTimeUpdate(args: any) { function audioTimeUpdate() {
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss'); 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> </script>
<template> <template>
@ -48,8 +96,10 @@ function audioTimeUpdate(args: any) {
:width="45" :width="45"
/> />
<div> <div>
<div>{{ currentSong.name }}</div> <div>{{ currentSong.title || '暂无音乐' }}</div>
<div class="text-xs text-gray-400">{{ currentSong.singer }}</div> <div class="text-xs text-gray-400">
{{ currentSong.singer || currentSong.desc }}
</div>
</div> </div>
</div> </div>
<!-- 音频controls --> <!-- 音频controls -->
@ -74,22 +124,25 @@ function audioTimeUpdate(args: any) {
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<span>{{ audioProps.currentTime }}</span> <span>{{ audioProps.currentTime }}</span>
<Slider <Slider
v-model:value="audioProps.duration" v-model:value="audioProgress"
:max="audioDuration"
color="#409eff" color="#409eff"
class="!w-40" class="!w-40"
@change="handleProgressChange"
/> />
<span>{{ audioProps.duration }}</span> <span>{{ audioProps.duration }}</span>
</div> </div>
<!-- 音频 --> <!-- 音频 -->
<audio <audio
v-bind="audioProps" :src="currentAudioUrl"
:autoplay="audioProps.autoplay"
:muted="audioProps.muted"
ref="audioRef" ref="audioRef"
controls controls
v-show="!audioProps" v-show="!audioProps"
@timeupdate="audioTimeUpdate" @timeupdate="audioTimeUpdate"
> @loadedmetadata="audioLoadedMetadata"
<!-- <source :src="audioUrl" /> --> ></audio>
</audio>
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<IconifyIcon <IconifyIcon

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Recordable } from '@vben/types'; import type { Recordable } from '@vben/types';
import type { MusicSong } from './types';
import { provide, ref } from 'vue'; 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 audioBar from './audioBar/index.vue';
import songCard from './songCard/index.vue'; import songCard from './songCard/index.vue';
import songInfo from './songInfo/index.vue'; import songInfo from './songInfo/index.vue';
import { currentSongKey } from './types';
defineOptions({ name: 'AiMusicListIndex' }); defineOptions({ name: 'AiMusicListIndex' });
const currentType = ref('mine'); const currentType = ref('mine');
const loading = ref(false); // loading const loading = ref(false); // loading
const currentSong = ref({}); // const currentSong = ref<MusicSong>({}); //
const mySongList = ref<Recordable<any>[]>([]); const mySongList = ref<MusicSong[]>([]);
const squareSongList = ref<Recordable<any>[]>([]); const squareSongList = ref<MusicSong[]>([]);
function generateMusic(_formData: Recordable<any>) { function generateMusic(_formData: Recordable<any>) {
loading.value = true; loading.value = true;
@ -45,7 +47,7 @@ function generateMusic(_formData: Recordable<any>) {
}, 3000); }, 3000);
} }
function setCurrentSong(music: Recordable<any>) { function setCurrentSong(music: MusicSong) {
currentSong.value = music; currentSong.value = music;
} }
@ -53,7 +55,7 @@ defineExpose({
generateMusic, generateMusic,
}); });
provide('currentSong', currentSong); provide(currentSongKey, currentSong);
</script> </script>
<template> <template>

View File

@ -1,22 +1,23 @@
<script lang="ts" setup> <script lang="ts" setup>
import { inject } from 'vue'; import type { MusicSong } from '../types';
import { inject, ref } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { Image } from 'antdv-next'; import { Image } from 'antdv-next';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicSongCardIndex' }); defineOptions({ name: 'AiMusicSongCardIndex' });
defineProps({ withDefaults(defineProps<{ songInfo?: MusicSong }>(), {
songInfo: { songInfo: () => ({}),
type: Object,
default: () => ({}),
},
}); });
const emits = defineEmits(['play']); const emits = defineEmits(['play']);
const currentSong = inject<any>('currentSong', {}); const currentSong = inject(currentSongKey, ref<MusicSong>({}));
function playSong() { function playSong() {
emits('play'); emits('play');

View File

@ -1,11 +1,15 @@
<script lang="ts" setup> <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 { Button, Card, Image } from 'antdv-next';
import { currentSongKey } from '../types';
defineOptions({ name: 'AiMusicSongInfoIndex' }); defineOptions({ name: 'AiMusicSongInfoIndex' });
const currentSong = inject<any>('currentSong', {}); const currentSong = inject(currentSongKey, ref<MusicSong>({}));
</script> </script>
<template> <template>

View 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');

View File

@ -245,6 +245,9 @@ const moddleExtensions = computed(() => {
const initBpmnModeler = () => { const initBpmnModeler = () => {
if (bpmnModeler) return; if (bpmnModeler) return;
const data: any = document.querySelector('#bpmnCanvas'); const data: any = document.querySelector('#bpmnCanvas');
if (!data) {
return;
}
// console.log(data, 'data'); // console.log(data, 'data');
// console.log(props.keyboard, 'props.keyboard'); // console.log(props.keyboard, 'props.keyboard');
// console.log(additionalModules, 'additionalModules()'); // console.log(additionalModules, 'additionalModules()');
@ -261,7 +264,7 @@ const initBpmnModeler = () => {
// propertiesPanel: { // propertiesPanel: {
// parent: '#js-properties-panel' // parent: '#js-properties-panel'
// }, // },
keyboard: props.keyboard ? { bindTo: document } : null, keyboard: props.keyboard ? { bind: true } : null,
// additionalModules: additionalModules.value, // additionalModules: additionalModules.value,
additionalModules: additionalModules.value as any[], additionalModules: additionalModules.value as any[],
moddleExtensions: moddleExtensions.value, moddleExtensions: moddleExtensions.value,

View File

@ -48,6 +48,25 @@ const dialogVisible = ref(false); // 弹窗可见性
const dialogTitle = ref<string | undefined>(undefined); // const dialogTitle = ref<string | undefined>(undefined); //
const selectActivityType = ref<string | undefined>(undefined); // Task const selectActivityType = ref<string | undefined>(undefined); // Task
const selectTasks = ref<any[]>([]); // 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 approvalColumns = computed<TableColumnType[]>(() => {
const userColumn: TableColumnType = const userColumn: TableColumnType =
selectActivityType.value === 'bpmn:UserTask' selectActivityType.value === 'bpmn:UserTask'
@ -126,7 +145,7 @@ const approvalColumns = computed<TableColumnType[]>(() => {
/** Zoom恢复 */ /** Zoom恢复 */
const processReZoom = () => { const processReZoom = () => {
defaultZoom.value = 1; defaultZoom.value = 1;
bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto'); getCanvas()?.zoom('fit-viewport', 'auto');
}; };
let resizeObserver: null | ResizeObserver = null; let resizeObserver: null | ResizeObserver = null;
@ -173,7 +192,7 @@ const processZoomIn = (zoomStep = 0.1) => {
); );
} }
defaultZoom.value = newZoom; defaultZoom.value = newZoom;
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value); getCanvas()?.zoom(defaultZoom.value);
}; };
/** Zoom缩小 */ /** Zoom缩小 */
@ -185,7 +204,7 @@ const processZoomOut = (zoomStep = 0.1) => {
); );
} }
defaultZoom.value = newZoom; defaultZoom.value = newZoom;
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value); getCanvas()?.zoom(defaultZoom.value);
}; };
/** 流程图预览清空 */ /** 流程图预览清空 */
@ -206,9 +225,9 @@ const addCustomDefs = () => {
if (!bpmnViewer.value) { if (!bpmnViewer.value) {
return; return;
} }
const canvas = bpmnViewer.value?.get('canvas'); const canvas = getCanvas();
const svg = canvas?._svg; const svg = canvas?._svg;
svg.append(customDefs.value); svg?.append(customDefs.value);
}; };
/** 节点选中 */ /** 节点选中 */
@ -304,8 +323,11 @@ const setProcessStatus = (view: any) => {
finishedSequenceFlowActivityIds, finishedSequenceFlowActivityIds,
rejectedTaskActivityIds, rejectedTaskActivityIds,
} = view; } = view;
const canvas: any = bpmnViewer.value.get('canvas'); const canvas = getCanvas();
const elementRegistry: any = bpmnViewer.value.get('elementRegistry'); const elementRegistry = getElementRegistry();
if (!canvas || !elementRegistry) {
return;
}
// //
if (Array.isArray(finishedSequenceFlowActivityIds)) { if (Array.isArray(finishedSequenceFlowActivityIds)) {
@ -313,7 +335,7 @@ const setProcessStatus = (view: any) => {
if (item !== null) { if (item !== null) {
canvas.addMarker(item, 'success'); canvas.addMarker(item, 'success');
const element = elementRegistry.get(item); const element = elementRegistry.get(item);
const conditionExpression = element.businessObject.conditionExpression; const conditionExpression = element?.businessObject.conditionExpression;
if (conditionExpression) { if (conditionExpression) {
canvas.addMarker(item, 'condition-expression'); canvas.addMarker(item, 'condition-expression');
} }

View File

@ -218,7 +218,7 @@ watch(
<template> <template>
<div> <div>
<Divider orientation="left">审批人超时未处理时</Divider> <Divider title-placement="left">审批人超时未处理时</Divider>
<FormItem label="启用开关" name="timeoutHandlerEnable"> <FormItem label="启用开关" name="timeoutHandlerEnable">
<Switch <Switch
v-model:checked="timeoutHandlerEnable" v-model:checked="timeoutHandlerEnable"

View File

@ -447,7 +447,7 @@ onMounted(async () => {
<template> <template>
<div> <div>
<Divider orientation="left">审批类型</Divider> <Divider title-placement="left">审批类型</Divider>
<FormItem name="approveType" label="审批类型"> <FormItem name="approveType" label="审批类型">
<RadioGroup v-model:value="approveType.value"> <RadioGroup v-model:value="approveType.value">
<Radio <Radio
@ -460,7 +460,7 @@ onMounted(async () => {
</RadioGroup> </RadioGroup>
</FormItem> </FormItem>
<Divider orientation="left">审批人拒绝时</Divider> <Divider title-placement="left">审批人拒绝时</Divider>
<FormItem name="rejectHandlerType" label="处理方式"> <FormItem name="rejectHandlerType" label="处理方式">
<RadioGroup <RadioGroup
v-model:value="rejectHandlerType" v-model:value="rejectHandlerType"
@ -492,7 +492,7 @@ onMounted(async () => {
/> />
</FormItem> </FormItem>
<Divider orientation="left">审批人为空时</Divider> <Divider title-placement="left">审批人为空时</Divider>
<FormItem name="assignEmptyHandlerType"> <FormItem name="assignEmptyHandlerType">
<RadioGroup <RadioGroup
v-model:value="assignEmptyHandlerType" v-model:value="assignEmptyHandlerType"
@ -523,7 +523,7 @@ onMounted(async () => {
/> />
</FormItem> </FormItem>
<Divider orientation="left">审批人与提交人为同一人时</Divider> <Divider title-placement="left">审批人与提交人为同一人时</Divider>
<RadioGroup <RadioGroup
v-model:value="assignStartUserHandlerType" v-model:value="assignStartUserHandlerType"
@change="updateAssignStartUserHandlerType" @change="updateAssignStartUserHandlerType"
@ -540,7 +540,7 @@ onMounted(async () => {
</div> </div>
</RadioGroup> </RadioGroup>
<Divider orientation="left">操作按钮</Divider> <Divider title-placement="left">操作按钮</Divider>
<div class="mt-2 text-sm"> <div class="mt-2 text-sm">
<!-- 头部标题行 --> <!-- 头部标题行 -->
<div <div
@ -587,7 +587,7 @@ onMounted(async () => {
</div> </div>
</div> </div>
<Divider orientation="left">字段权限</Divider> <Divider title-placement="left">字段权限</Divider>
<div v-if="formType === BpmModelFormType.NORMAL" class="mt-2 text-sm"> <div v-if="formType === BpmModelFormType.NORMAL" class="mt-2 text-sm">
<!-- 头部标题行 --> <!-- 头部标题行 -->
<div <div
@ -663,7 +663,7 @@ onMounted(async () => {
</div> </div>
</div> </div>
<Divider orientation="left">是否需要签名</Divider> <Divider title-placement="left">是否需要签名</Divider>
<FormItem name="signEnable"> <FormItem name="signEnable">
<Switch <Switch
v-model:checked="signEnable.value" v-model:checked="signEnable.value"
@ -673,7 +673,7 @@ onMounted(async () => {
/> />
</FormItem> </FormItem>
<Divider orientation="left">审批意见</Divider> <Divider title-placement="left">审批意见</Divider>
<FormItem name="reasonRequire"> <FormItem name="reasonRequire">
<Switch <Switch
v-model:checked="reasonRequire.value" v-model:checked="reasonRequire.value"

View File

@ -61,8 +61,8 @@ interface LoopInstanceForm {
} }
const loopInstanceForm = ref<LoopInstanceForm>({}); const loopInstanceForm = ref<LoopInstanceForm>({});
const bpmnElement = ref<any>(null); const bpmnElement = ref<any | null>(null);
const multiLoopInstance = ref<any>(null); const multiLoopInstance = ref<any | null>(null);
declare global { declare global {
interface Window { interface Window {
bpmnInstances?: () => any; bpmnInstances?: () => any;
@ -276,7 +276,7 @@ const approveMethod = ref<ApproveMethodType | undefined>();
const approveRatio = ref<number>(100); const approveRatio = ref<number>(100);
const otherExtensions = ref<any[]>([]); const otherExtensions = ref<any[]>([]);
const getElementLoopNew = (): void => { const getElementLoopNew = (): void => {
if (props.type === 'UserTask') { if (props.type === 'UserTask' && bpmnElement.value) {
const loopCharacteristics = const loopCharacteristics =
bpmnElement.value.businessObject?.loopCharacteristics; bpmnElement.value.businessObject?.loopCharacteristics;
const extensionElements = const extensionElements =
@ -320,6 +320,9 @@ const onApproveRatioChange = (): void => {
updateLoopCharacteristics(); updateLoopCharacteristics();
}; };
const updateLoopCharacteristics = (): void => { const updateLoopCharacteristics = (): void => {
if (!bpmnElement.value) {
return;
}
// ApproveMethodmultiInstanceLoopCharacteristics // ApproveMethodmultiInstanceLoopCharacteristics
if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) { if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
@ -367,9 +370,11 @@ const updateLoopCharacteristics = (): void => {
body: `\${ nrOfCompletedInstances >= nrOfInstances }`, body: `\${ nrOfCompletedInstances >= nrOfInstances }`,
}); });
} }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { if (multiLoopInstance.value) {
loopCharacteristics: toRaw(multiLoopInstance.value), bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
}); loopCharacteristics: toRaw(multiLoopInstance.value),
});
}
} }
// ApproveMethodExtensionElements // ApproveMethodExtensionElements

View File

@ -186,6 +186,12 @@ const multiFormFieldOptions = computed(() => {
(item) => item.type === 'select' || item.type === 'checkbox', (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[]>([]); const childFormFieldOptions = ref<any[]>([]);
/** 保存配置 */ /** 保存配置 */
@ -781,7 +787,7 @@ onMounted(async () => {
]" ]"
> >
<InputNumber <InputNumber
v-model:value="configForm.multiInstanceSource" v-model:value="multiInstanceSourceNumber"
:min="1" :min="1"
/> />
</FormItem> </FormItem>

View File

@ -66,7 +66,7 @@ defineExpose({ validate });
:key="listenerIdx" :key="listenerIdx"
class="pl-2" class="pl-2"
> >
<Divider orientation="left"> <Divider title-placement="left">
<TypographyText tag="b" size="large"> <TypographyText tag="b" size="large">
{{ listener.name }} {{ listener.name }}
</TypographyText> </TypographyText>

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