Merge remote-tracking branch 'yudao/dev' into dev

This commit is contained in:
jason
2025-10-25 20:35:52 +08:00
835 changed files with 44384 additions and 9756 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 495 KiB

View File

@@ -10,7 +10,7 @@ export namespace BpmCategoryApi {
code: string; code: string;
status: number; status: number;
description?: string; description?: string;
sort: number; // 分类排序 sort: number;
} }
} }

View File

@@ -1,23 +1,31 @@
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import type { BpmModelApi } from '#/api/bpm/model';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace BpmProcessDefinitionApi { export namespace BpmProcessDefinitionApi {
/** 流程定义 */ /** 流程定义 */
export interface ProcessDefinition { export interface ProcessDefinition {
id: string; id: string;
key?: string;
version: number; version: number;
name: string; name: string;
category: string;
description: string; description: string;
deploymentTime: number; deploymentTime: number;
suspensionState: number; suspensionState: number;
modelType: number; modelType: number;
modelId: string; modelId: string;
formType?: number; formType?: number;
formId?: number;
formName?: string;
formCustomCreatePath?: string;
bpmnXml?: string; bpmnXml?: string;
simpleModel?: string; simpleModel?: string;
formFields?: string[]; formFields?: string[];
icon?: string; icon?: string;
startUsers?: BpmModelApi.UserInfo[];
} }
} }

View File

@@ -5,7 +5,7 @@ import { requestClient } from '#/api/request';
export namespace BpmFormApi { export namespace BpmFormApi {
/** 流程表单 */ /** 流程表单 */
export interface Form { export interface Form {
id?: number | undefined; id?: number;
name: string; name: string;
conf: string; conf: string;
fields: string[]; fields: string[];
@@ -23,7 +23,7 @@ export async function getFormPage(params: PageParam) {
} }
/** 获取表单详情 */ /** 获取表单详情 */
export async function getFormDetail(id: number) { export async function getForm(id: number) {
return requestClient.get<BpmFormApi.Form>(`/bpm/form/get?id=${id}`); return requestClient.get<BpmFormApi.Form>(`/bpm/form/get?id=${id}`);
} }

View File

@@ -52,7 +52,7 @@ export interface ModelCategoryInfo {
} }
/** 获取流程模型列表 */ /** 获取流程模型列表 */
export async function getModelList(name: string | undefined) { export async function getModelList(name?: string) {
return requestClient.get<BpmModelApi.Model[]>('/bpm/model/list', { return requestClient.get<BpmModelApi.Model[]>('/bpm/model/list', {
params: { name }, params: { name },
}); });

View File

@@ -15,6 +15,7 @@ export namespace BpmProcessInstanceApi {
export interface Task { export interface Task {
id: number; id: number;
name: string; name: string;
assigneeUser?: User;
} }
export interface User { export interface User {
@@ -51,6 +52,7 @@ export namespace BpmProcessInstanceApi {
export interface ProcessInstance { export interface ProcessInstance {
businessKey: string; businessKey: string;
category: string; category: string;
categoryName?: string;
createTime: string; createTime: string;
endTime: string; endTime: string;
fields: string[]; fields: string[];
@@ -64,6 +66,10 @@ export namespace BpmProcessInstanceApi {
startTime?: Date; startTime?: Date;
startUser?: User; startUser?: User;
status: number; status: number;
summary?: {
key: string;
value: string;
}[];
tasks?: BpmProcessInstanceApi.Task[]; tasks?: BpmProcessInstanceApi.Task[];
} }

View File

@@ -13,6 +13,7 @@ export namespace BpmTaskApi {
status: number; // 监听器状态 status: number; // 监听器状态
event: string; // 监听事件 event: string; // 监听事件
valueType: string; // 监听器值类型 valueType: string; // 监听器值类型
processInstance?: BpmProcessInstanceApi.ProcessInstance; // 流程实例
} }
// 流程任务 // 流程任务

View File

@@ -20,9 +20,9 @@ export namespace ErpFinancePaymentApi {
export interface FinancePayment { export interface FinancePayment {
id?: number; // 付款单编号 id?: number; // 付款单编号
no: string; // 付款单号 no: string; // 付款单号
supplierId: number; // 供应商编号 supplierId?: number; // 供应商编号
supplierName?: string; // 供应商名称 supplierName?: string; // 供应商名称
paymentTime: Date; // 付款时间 paymentTime?: Date; // 付款时间
totalPrice: number; // 合计金额,单位:元 totalPrice: number; // 合计金额,单位:元
discountPrice: number; // 优惠金额 discountPrice: number; // 优惠金额
paymentPrice: number; // 实际付款金额 paymentPrice: number; // 实际付款金额

View File

@@ -2,7 +2,7 @@ import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
namespace ErpFinanceReceiptApi { export namespace ErpFinanceReceiptApi {
/** 收款单项 */ /** 收款单项 */
export interface FinanceReceiptItem { export interface FinanceReceiptItem {
id?: number; id?: number;

View File

@@ -95,7 +95,7 @@ export function getOtaTaskRecordStatusStatistics(
taskId?: number, taskId?: number,
) { ) {
return requestClient.get<Record<string, number>>( return requestClient.get<Record<string, number>>(
'/iot/ota/task/record/status-statistics', '/iot/ota/task/record/get-status-statistics',
{ params: { firmwareId, taskId } }, { params: { firmwareId, taskId } },
); );
} }

View File

@@ -101,6 +101,7 @@ export interface Action {
identifier?: string; identifier?: string;
value?: any; value?: any;
alertConfigId?: number; alertConfigId?: number;
params?: string;
} }
/** 查询场景联动规则分页 */ /** 查询场景联动规则分页 */

View File

@@ -175,17 +175,27 @@ export function deleteThingModelList(ids: number[]) {
}); });
} }
/** 导入物模型 TSL */ /** 获取物模型 TSL */
export function getThingModelTSL(productId: number) {
return requestClient.get<ThingModelApi.ThingModel[]>(
'/iot/thing-model/get-tsl',
{ params: { productId } },
);
}
/** 导入物模型 TSL
export function importThingModelTSL(productId: number, tslData: any) { export function importThingModelTSL(productId: number, tslData: any) {
return requestClient.post('/iot/thing-model/import-tsl', { return requestClient.post('/iot/thing-model/import-tsl', {
productId, productId,
tslData, tslData,
}); });
} }
*/
/** 导出物模型 TSL */ /** 导出物模型 TSL
export function exportThingModelTSL(productId: number) { export function exportThingModelTSL(productId: number) {
return requestClient.get<any>('/iot/thing-model/export-tsl', { return requestClient.get<any>('/iot/thing-model/export-tsl', {
params: { productId }, params: { productId },
}); });
} }
*/

View File

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

View File

@@ -1,13 +1,15 @@
import type { MallDataComparisonResp } from './common'; import type { Dayjs } from 'dayjs';
import { formatDate } from '@vben/utils'; import type { DataComparisonRespVO } from './common';
import { formatDateTime } from '@vben/utils';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace MallMemberStatisticsApi { export namespace MallMemberStatisticsApi {
/** 会员分析 Request */ /** 会员分析 Request */
export interface AnalyseReq { export interface MemberAnalyseReqVO {
times: Date[]; // 时间范围 times: Date[] | Dayjs[]; // 时间范围
} }
/** 会员分析对照数据 Response */ /** 会员分析对照数据 Response */
@@ -23,7 +25,7 @@ export namespace MallMemberStatisticsApi {
orderUserCount: number; // 下单用户数 orderUserCount: number; // 下单用户数
payUserCount: number; // 支付用户数 payUserCount: number; // 支付用户数
atv: number; // 平均客单价 atv: number; // 平均客单价
comparison: MallDataComparisonResp<AnalyseComparison>; // 对照数据 comparison: DataComparisonRespVO<AnalyseComparison>; // 对照数据
} }
/** 会员地区统计 Response */ /** 会员地区统计 Response */
@@ -57,7 +59,7 @@ export namespace MallMemberStatisticsApi {
} }
/** 会员数量统计 Response */ /** 会员数量统计 Response */
export interface Count { export interface MemberCountRespVO {
visitUserCount: string; // 用户访问量 visitUserCount: string; // 用户访问量
registerUserCount: number; // 注册用户数量 registerUserCount: number; // 注册用户数量
} }
@@ -77,12 +79,17 @@ export function getMemberSummary() {
} }
/** 查询会员分析数据 */ /** 查询会员分析数据 */
export function getMemberAnalyse(params: MallMemberStatisticsApi.AnalyseReq) { export function getMemberAnalyse(
params: MallMemberStatisticsApi.MemberAnalyseReqVO,
) {
return requestClient.get<MallMemberStatisticsApi.Analyse>( return requestClient.get<MallMemberStatisticsApi.Analyse>(
'/statistics/member/analyse', '/statistics/member/analyse',
{ {
params: { params: {
times: [formatDate(params.times[0]), formatDate(params.times[1])], times: [
formatDateTime(params.times[0]),
formatDateTime(params.times[1]),
],
}, },
}, },
); );
@@ -112,7 +119,7 @@ export function getMemberTerminalStatisticsList() {
/** 获得用户数量量对照 */ /** 获得用户数量量对照 */
export function getUserCountComparison() { export function getUserCountComparison() {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallMemberStatisticsApi.Count> DataComparisonRespVO<MallMemberStatisticsApi.MemberCountRespVO>
>('/statistics/member/user-count-comparison'); >('/statistics/member/user-count-comparison');
} }
@@ -122,7 +129,7 @@ export function getMemberRegisterCountList(beginTime: Date, endTime: Date) {
'/statistics/member/register-count-list', '/statistics/member/register-count-list',
{ {
params: { params: {
times: [formatDate(beginTime), formatDate(endTime)], times: [formatDateTime(beginTime), formatDateTime(endTime)],
}, },
}, },
); );

View File

@@ -1,6 +1,6 @@
import type { PageParam, PageResult } from '@vben/request'; import type { PageParam, PageResult } from '@vben/request';
import type { MallDataComparisonResp } from './common'; import type { DataComparisonRespVO } from './common';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@@ -41,14 +41,14 @@ export namespace MallProductStatisticsApi {
} }
/** 获得商品统计分析 */ /** 获得商品统计分析 */
export function getProductStatisticsAnalyse(params: PageParam) { export function getProductStatisticsAnalyse(params: any) {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallProductStatisticsApi.ProductStatistics> DataComparisonRespVO<MallProductStatisticsApi.ProductStatistics>
>('/statistics/product/analyse', { params }); >('/statistics/product/analyse', { params });
} }
/** 获得商品状况明细 */ /** 获得商品状况明细 */
export function getProductStatisticsList(params: PageParam) { export function getProductStatisticsList(params: any) {
return requestClient.get<MallProductStatisticsApi.ProductStatistics[]>( return requestClient.get<MallProductStatisticsApi.ProductStatistics[]>(
'/statistics/product/list', '/statistics/product/list',
{ params }, { params },
@@ -56,7 +56,7 @@ export function getProductStatisticsList(params: PageParam) {
} }
/** 导出获得商品状况明细 Excel */ /** 导出获得商品状况明细 Excel */
export function exportProductStatisticsExcel(params: PageParam) { export function exportProductStatisticsExcel(params: any) {
return requestClient.download('/statistics/product/export-excel', { params }); return requestClient.download('/statistics/product/export-excel', { params });
} }

View File

@@ -1,6 +1,6 @@
import type { MallDataComparisonResp } from './common'; import type { DataComparisonRespVO } from './common';
import { formatDate } from '@vben/utils'; import { formatDate, formatDateTime } from '@vben/utils';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
@@ -43,7 +43,7 @@ export namespace MallTradeStatisticsApi {
} }
/** 交易订单统计 Response */ /** 交易订单统计 Response */
export interface TradeOrderSummary { export interface TradeOrderSummaryRespVO {
/** 支付订单商品数 */ /** 支付订单商品数 */
orderPayCount?: number; orderPayCount?: number;
/** 总支付金额,单位:分 */ /** 总支付金额,单位:分 */
@@ -71,36 +71,28 @@ const formatDateParam = (params: MallTradeStatisticsApi.TradeTrendReq) => {
/** 查询交易统计 */ /** 查询交易统计 */
export function getTradeStatisticsSummary() { export function getTradeStatisticsSummary() {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallTradeStatisticsApi.TradeSummary> DataComparisonRespVO<MallTradeStatisticsApi.TradeSummary>
>('/statistics/trade/summary'); >('/statistics/trade/summary');
} }
/** 获得交易状况统计 */ /** 获得交易状况统计 */
export function getTradeStatisticsAnalyse( export function getTradeStatisticsAnalyse(params: any) {
params: MallTradeStatisticsApi.TradeTrendReq,
) {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallTradeStatisticsApi.TradeTrendSummary> DataComparisonRespVO<MallTradeStatisticsApi.TradeTrendSummary>
>('/statistics/trade/analyse', { params: formatDateParam(params) }); >('/statistics/trade/analyse', { params });
} }
/** 获得交易状况明细 */ /** 获得交易状况明细 */
export function getTradeStatisticsList( export function getTradeStatisticsList(params: any) {
params: MallTradeStatisticsApi.TradeTrendReq,
) {
return requestClient.get<MallTradeStatisticsApi.TradeTrendSummary[]>( return requestClient.get<MallTradeStatisticsApi.TradeTrendSummary[]>(
'/statistics/trade/list', '/statistics/trade/list',
{ params: formatDateParam(params) }, { params },
); );
} }
/** 导出交易状况明细 */ /** 导出交易状况明细 */
export function exportTradeStatisticsExcel( export function exportTradeStatisticsExcel(params: any) {
params: MallTradeStatisticsApi.TradeTrendReq, return requestClient.download('/statistics/trade/export-excel', { params });
) {
return requestClient.download('/statistics/trade/export-excel', {
params: formatDateParam(params),
});
} }
/** 获得交易订单数量 */ /** 获得交易订单数量 */
@@ -113,7 +105,7 @@ export function getOrderCount() {
/** 获得交易订单数量对照 */ /** 获得交易订单数量对照 */
export function getOrderComparison() { export function getOrderComparison() {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallTradeStatisticsApi.TradeOrderSummary> DataComparisonRespVO<MallTradeStatisticsApi.TradeOrderSummaryRespVO>
>('/statistics/trade/order-comparison'); >('/statistics/trade/order-comparison');
} }
@@ -124,12 +116,12 @@ export function getOrderCountTrendComparison(
endTime: Date, endTime: Date,
) { ) {
return requestClient.get< return requestClient.get<
MallDataComparisonResp<MallTradeStatisticsApi.TradeOrderTrend>[] DataComparisonRespVO<MallTradeStatisticsApi.TradeOrderTrend>[]
>('/statistics/trade/order-count-trend', { >('/statistics/trade/order-count-trend', {
params: { params: {
type, type,
beginTime: formatDate(beginTime), beginTime: formatDateTime(beginTime),
endTime: formatDate(endTime), endTime: formatDateTime(endTime),
}, },
}); });
} }

View File

@@ -1,13 +1,10 @@
import type { PageParam } from '@vben/request';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace MpStatisticsApi { export namespace MpStatisticsApi {
/** 统计查询参数 */ /** 统计查询参数 */
export interface StatisticsQuery extends PageParam { export interface StatisticsQuery {
accountId: number; accountId: number;
beginDate: string; date: Date[];
endDate: string;
} }
/** 消息发送概况数据 */ /** 消息发送概况数据 */

View File

@@ -35,6 +35,7 @@ async function bootstrap(namespace: string) {
// }); // });
const app = createApp(App); const app = createApp(App);
app.use(VueDOMPurifyHTML);
// 注册v-loading指令 // 注册v-loading指令
registerLoadingDirective(app, { registerLoadingDirective(app, {
@@ -61,10 +62,6 @@ async function bootstrap(namespace: string) {
// formCreate // formCreate
setupFormCreate(app); setupFormCreate(app);
// vue-dompurify-html
// TODO @dhb52VueDOMPurifyHTML 是不是不用引入哈?
app.use(VueDOMPurifyHTML);
// 配置Motion插件 // 配置Motion插件
const { MotionPlugin } = await import('@vben/plugins/motion'); const { MotionPlugin } = await import('@vben/plugins/motion');
app.use(MotionPlugin); app.use(MotionPlugin);

View File

@@ -1,13 +0,0 @@
import './theme/index.scss';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
// TODO @puhui999样式问题设计器那位置不太对
export { default as MyProcessDesigner } from './designer';
// TODO @puhui999流程发起时预览相关的需要使用
export { default as MyProcessViewer } from './designer/index2';
export { default as MyProcessPenal } from './penal';
// TODO @puhui999【有个迁移的打印】【新增】流程打印由 [@Lesan](https://gitee.com/LesanOuO) 贡献 [#816](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/816/)、[#1418](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1418/)、[#817](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/817/)、[#1419](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1419/)、[#1424](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1424)、[#819](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/819)、[#821](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/821/)

View File

@@ -1,82 +1,196 @@
<script lang="tsx"> <script lang="tsx">
import type { DescriptionsProps } from 'ant-design-vue'; import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
import type { PropType } from 'vue'; import type { CSSProperties, PropType, Slots } from 'vue';
import type { DescriptionItemSchema, DescriptionsOptions } from './typing'; import type { DescriptionItemSchema, DescriptionProps } from './typing';
import { defineComponent } from 'vue'; import { computed, defineComponent, ref, unref, useAttrs } from 'vue';
import { get } from '@vben/utils'; import { get, getNestedValue, isFunction } from '@vben/utils';
import { Descriptions, DescriptionsItem } from 'ant-design-vue'; import { Card, Descriptions } from 'ant-design-vue';
/** 对 Descriptions 进行二次封装 */ const props = {
const Description = defineComponent({ bordered: { default: true, type: Boolean },
name: 'Descriptions', column: {
props: { default: () => {
data: { return { lg: 3, md: 3, sm: 2, xl: 3, xs: 1, xxl: 4 };
type: Object as PropType<Record<string, any>>,
default: () => ({}),
},
schema: {
type: Array as PropType<DescriptionItemSchema[]>,
default: () => [],
},
// Descriptions 原生 props
componentProps: {
type: Object as PropType<DescriptionsProps>,
default: () => ({}),
}, },
type: [Number, Object],
}, },
data: { type: Object },
schema: {
default: () => [],
type: Array as PropType<DescriptionItemSchema[]>,
},
size: {
default: 'small',
type: String,
validator: (v: string) =>
['default', 'middle', 'small', undefined].includes(v),
},
title: { default: '', type: String },
useCard: { default: true, type: Boolean },
};
setup(props: DescriptionsOptions) { function getSlot(slots: Slots, slot: string, data?: any) {
// TODO @xingyu每个 field 的 slot 的考虑 if (!slots || !Reflect.has(slots, slot)) {
// TODO @xingyufrom 5.0extra: () => getSlot(slots, 'extra') return null;
/** 过滤掉不需要展示的 */ }
const shouldShowItem = (item: DescriptionItemSchema) => { if (!isFunction(slots[slot])) {
if (item.hidden === undefined) return true; console.error(`${slot} is not a function!`);
return typeof item.hidden === 'function' return null;
? !item.hidden(props.data) }
: !item.hidden; const slotFn = slots[slot];
}; if (!slotFn) return null;
/** 渲染内容 */ return slotFn({ data });
const renderContent = (item: DescriptionItemSchema) => { }
if (item.content) {
return typeof item.content === 'function' export default defineComponent({
? item.content(props.data) name: 'Description',
: item.content; props,
setup(props, { slots }) {
const propsRef = ref<null | Partial<DescriptionProps>>(null);
const prefixCls = 'description';
const attrs = useAttrs();
// Custom title component: get title
const getMergeProps = computed(() => {
return {
...props,
...(unref(propsRef) as any),
} as DescriptionProps;
});
const getProps = computed(() => {
const opt = {
...unref(getMergeProps),
title: undefined,
};
return opt as DescriptionProps;
});
const useWrapper = computed(() => !!unref(getMergeProps).title);
const getDescriptionsProps = computed(() => {
return { ...unref(attrs), ...unref(getProps) } as DescriptionsProps;
});
// 防止换行
function renderLabel({
label,
labelMinWidth,
labelStyle,
}: DescriptionItemSchema) {
if (!labelStyle && !labelMinWidth) {
return label;
} }
return item.field ? get(props.data, item.field) : null;
};
return () => ( const labelStyles: CSSProperties = {
<Descriptions ...labelStyle,
{...props} minWidth: `${labelMinWidth}px `,
bordered={props.componentProps?.bordered} };
colon={props.componentProps?.colon} return <div style={labelStyles}>{label}</div>;
column={props.componentProps?.column} }
extra={props.componentProps?.extra}
layout={props.componentProps?.layout} function renderItem() {
size={props.componentProps?.size} const { data, schema } = unref(getProps);
title={props.componentProps?.title} return unref(schema)
> .map((item) => {
{props.schema?.filter(shouldShowItem).map((item) => ( const { contentMinWidth, field, render, show, span } = item;
<DescriptionsItem
contentStyle={item.contentStyle} if (show && isFunction(show) && !show(data)) {
key={item.field || String(item.label)} return null;
label={item.label} }
labelStyle={item.labelStyle}
span={item.span} function getContent() {
> const _data = unref(getProps)?.data;
{renderContent(item)} if (!_data) {
</DescriptionsItem> return null;
))} }
</Descriptions> const getField = field.includes('.')
); ? (getNestedValue(_data, field) ?? get(_data, field))
: get(_data, field);
// if (
// getField &&
// !Object.prototype.hasOwnProperty.call(toRefs(_data), field)
// ) {
// return isFunction(render) ? render('', _data) : (getField ?? '');
// }
return isFunction(render)
? render(getField, _data)
: (getField ?? '');
}
const width = contentMinWidth;
return (
<Descriptions.Item
key={field}
label={renderLabel(item)}
span={span}
>
{() => {
if (item.slot) {
const slotContent = getSlot(slots, item.slot, data);
return slotContent;
}
if (!contentMinWidth) {
return getContent();
}
const style: CSSProperties = {
minWidth: `${width}px`,
};
return <div style={style}>{getContent()}</div>;
}}
</Descriptions.Item>
);
})
.filter((item) => !!item);
}
function renderDesc() {
return (
<Descriptions
class={`${prefixCls}`}
{...(unref(getDescriptionsProps) as any)}
>
{renderItem()}
</Descriptions>
);
}
function renderCard() {
const content = props.useCard ? renderDesc() : <div>{renderDesc()}</div>;
// Reduce the dom level
if (!props.useCard) {
return content;
}
const { title } = unref(getMergeProps);
const extraSlot = getSlot(slots, 'extra');
return (
<Card
bodyStyle={{ padding: '8px 0' }}
headStyle={{
padding: '8px 16px',
fontSize: '14px',
minHeight: '24px',
}}
style={{ margin: 0 }}
title={title}
>
{{
default: () => content,
extra: () => extraSlot && <div>{extraSlot}</div>,
}}
</Card>
);
}
return () => (unref(useWrapper) ? renderCard() : renderDesc());
}, },
}); });
// TODO @xingyufrom 5.0emits: ['register'] 事件
export default Description;
</script> </script>

View File

@@ -1,27 +1,56 @@
import type { DescriptionsProps } from 'ant-design-vue'; import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
import type { JSX } from 'vue/jsx-runtime';
import type { CSSProperties, VNode } from 'vue'; import type { CSSProperties, VNode } from 'vue';
// TODO @xingyu【content】这个纠结下1vben2.0 是 renderhttps://doc.vvbin.cn/components/desc.html#usage 2 import type { Recordable } from '@vben/types';
// TODO @xingyuvben2.0 还有 sapn【done】、labelMinWidth、contentMinWidth
// TODO @xingyu【hidden】这个纠结下1vben2.0 是 show
export interface DescriptionItemSchema { export interface DescriptionItemSchema {
label: string | VNode; // 内容的描述 labelMinWidth?: number;
field?: string; // 对应 data 中的字段名 contentMinWidth?: number;
content?: ((data: any) => string | VNode) | string | VNode; // 自定义需要展示的内容,比如说 dict-tag // 自定义标签样式
span?: number; // 包含列的数量 labelStyle?: CSSProperties;
labelStyle?: CSSProperties; // 自定义标签样式 // 对应 data 中的字段名
contentStyle?: CSSProperties; // 自定义内容样式 field: string;
hidden?: ((data: any) => boolean) | boolean; // 是否显示 // 内容的描述
label: JSX.Element | string | VNode;
// 包含列的数量
span?: number;
// 是否显示
show?: (...arg: any) => boolean;
// 插槽名称
slot?: string;
// 自定义需要展示的内容
render?: (
val: any,
data?: Recordable<any>,
) => Element | JSX.Element | number | string | undefined | VNode;
} }
// TODO @xingyuvben2.0 还有 title【done】、bordered【done】d、useCollapse、collapseOptions export interface DescriptionProps extends DescriptionsProps {
// TODO @xingyufrom 5.0bordered 默认为 true // 是否包含卡片组件
// TODO @xingyufrom 5.0column 默认为 lg: 3, md: 3, sm: 2, xl: 3, xs: 1, xxl: 4 useCard?: boolean;
// TODO @xingyufrom 5.0size 默认为 small有 'default', 'middle', 'small', undefined // 描述项配置
// TODO @xingyufrom 5.0useCollapse 默认为 true schema: DescriptionItemSchema[];
export interface DescriptionsOptions { // 数据
data?: Record<string, any>; // 数据 data: Recordable<any>;
schema?: DescriptionItemSchema[]; // 描述项配置 // 标题
componentProps?: DescriptionsProps; // antd Descriptions 组件参数 title?: string;
// 是否包含边框
bordered?: boolean;
// 列数
column?:
| number
| {
lg: number;
md: number;
sm: number;
xl: number;
xs: number;
xxl: number;
};
}
export interface DescInstance {
setDescProps(descProps: Partial<DescriptionProps>): void;
} }

View File

@@ -1,71 +1,31 @@
import type { DescriptionsOptions } from './typing'; import type { Component } from 'vue';
import { defineComponent, h, isReactive, reactive, watch } from 'vue'; import type { DescInstance, DescriptionProps } from './typing';
import { h, reactive } from 'vue';
import Description from './description.vue'; import Description from './description.vue';
/** 描述列表 api 定义 */ export function useDescription(options?: Partial<DescriptionProps>) {
class DescriptionApi { const propsState = reactive<Partial<DescriptionProps>>(options || {});
private state = reactive<Record<string, any>>({});
constructor(options: DescriptionsOptions) { const api: DescInstance = {
this.state = { ...options }; setDescProps: (descProps: Partial<DescriptionProps>): void => {
} Object.assign(propsState, descProps);
},
};
getState(): DescriptionsOptions { // 创建一个包装组件,将 propsState 合并到 props 中
return this.state as DescriptionsOptions; const DescriptionWrapper: Component = {
}
// TODO @xingyu【setState】纠结下1vben2.0 是 data https://doc.vvbin.cn/components/desc.html#usage
setState(newState: Partial<DescriptionsOptions>) {
this.state = { ...this.state, ...newState };
}
}
export type ExtendedDescriptionApi = DescriptionApi;
export function useDescription(options: DescriptionsOptions) {
const IS_REACTIVE = isReactive(options);
const api = new DescriptionApi(options);
// 扩展 API
const extendedApi: ExtendedDescriptionApi = api as never;
const Desc = defineComponent({
name: 'UseDescription', name: 'UseDescription',
inheritAttrs: false, inheritAttrs: false,
setup(_, { attrs, slots }) { setup(_props, { attrs, slots }) {
// 合并props和attrs到state return () => {
api.setState({ ...attrs }); // @ts-ignore - 避免类型实例化过深
return h(Description, { ...propsState, ...attrs }, slots);
return () => };
h(
Description,
{
...api.getState(),
...attrs,
},
slots,
);
}, },
}); };
// 响应式支持 return [DescriptionWrapper, api] as const;
if (IS_REACTIVE) {
watch(
() => options.schema,
(newSchema) => {
api.setState({ schema: newSchema });
},
{ immediate: true, deep: true },
);
watch(
() => options.data,
(newData) => {
api.setState({ data: newData });
},
{ immediate: true, deep: true },
);
}
return [Desc, extendedApi] as const;
} }

View File

@@ -202,7 +202,7 @@ export function useApiSelect(option: ApiSelectProps) {
onUpdate:value={onUpdateModelValue as any} onUpdate:value={onUpdateModelValue as any}
value={modelValue as any} value={modelValue as any}
{...restAttrs} {...restAttrs}
// TODO: remote 对等实现 // TODO @xingyu remote 对等实现, 还是说没作用
// remote={props.remote} // remote={props.remote}
{...(props.remote && { remoteMethod })} {...(props.remote && { remoteMethod })}
> >
@@ -223,7 +223,7 @@ export function useApiSelect(option: ApiSelectProps) {
onUpdate:value={onUpdateModelValue as any} onUpdate:value={onUpdateModelValue as any}
value={modelValue as any} value={modelValue as any}
{...restAttrs} {...restAttrs}
// TODO: @dhb52 remote 对等实现, 还是说没作用 // TODO: @xingyu remote 对等实现, 还是说没作用
// remote={props.remote} // remote={props.remote}
{...(props.remote && { remoteMethod })} {...(props.remote && { remoteMethod })}
> >

View File

@@ -1,9 +1,7 @@
import type { Rule } from '@form-create/ant-design-vue';
/** 数据字典 Select 选择器组件 Props 类型 */ /** 数据字典 Select 选择器组件 Props 类型 */
export interface DictSelectProps { export interface DictSelectProps {
dictType: string; // 字典类型 dictType: string; // 字典类型
valueType?: 'bool' | 'int' | 'str'; // 字典值类型 TODO @芋艿:'boolean' | 'number' | 'string';需要和 vue3 一起统一! valueType?: 'bool' | 'int' | 'str'; // 字典值类型
selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
formCreateInject?: any; formCreateInject?: any;
} }
@@ -22,25 +20,6 @@ export interface Menu {
list: MenuItem[]; list: MenuItem[];
} }
export type MenuList = Array<Menu>;
// TODO @dhb52MenuList、Menu、MenuItem、DragRule 这几个,是不是没用到呀?
// 拖拽组件的规则
export interface DragRule {
icon: string;
name: string;
label: string;
children?: string;
inside?: true;
drag?: string | true;
dragBtn?: false;
mask?: false;
rule(): Rule;
props(v: any, v1: any): Rule[];
}
/** 通用 API 下拉组件 Props 类型 */ /** 通用 API 下拉组件 Props 类型 */
export interface ApiSelectProps { export interface ApiSelectProps {
name: string; // 组件名称 name: string; // 组件名称

View File

@@ -0,0 +1 @@
export { default as ShortcutDateRangePicker } from './shortcut-date-range-picker.vue';

View File

@@ -0,0 +1,89 @@
<script lang="ts" setup>
import type { Dayjs } from 'dayjs';
import { onMounted, ref } from 'vue';
import { DatePicker, Radio, RadioGroup } from 'ant-design-vue';
import { getRangePickerDefaultProps } from '#/utils/rangePickerProps';
/** 快捷日期范围选择组件 */
defineOptions({ name: 'ShortcutDateRangePicker' });
const emits = defineEmits<{
change: [times: [Dayjs, Dayjs]];
}>();
const times = ref<[Dayjs, Dayjs]>(); // 日期范围
const rangePickerProps = getRangePickerDefaultProps();
const timeRangeOptions = [
rangePickerProps.presets[3]!, // 昨天
rangePickerProps.presets[1]!, // 最近 7 天
rangePickerProps.presets[2]!, // 最近 30 天
];
const timeRangeType = ref(timeRangeOptions[1]!.label); // 默认选中第一个选项
/** 设置时间范围 */
function setTimes() {
// 根据选中的选项设置时间范围
const selectedOption = timeRangeOptions.find(
(option) => option.label === timeRangeType.value,
);
if (selectedOption) {
times.value = selectedOption.value as [Dayjs, Dayjs];
}
}
/** 快捷日期单选按钮选中 */
async function handleShortcutDaysChange() {
// 设置时间范围
setTimes();
// 触发时间范围选中事件
emitDateRangePicker();
}
/** 日期范围改变 */
function handleDateRangeChange() {
emitDateRangePicker();
}
/** 触发时间范围选中事件 */
function emitDateRangePicker() {
if (times.value && times.value.length === 2) {
emits('change', times.value);
}
}
/** 初始化 */
onMounted(() => {
handleShortcutDaysChange();
});
</script>
<template>
<div class="flex items-center gap-2">
<RadioGroup
v-model:value="timeRangeType"
@change="handleShortcutDaysChange"
>
<Radio
v-for="option in timeRangeOptions"
:key="option.label"
:value="option.label"
>
{{ option.label }}
</Radio>
</RadioGroup>
<DatePicker.RangePicker
v-model:value="times"
:format="rangePickerProps.format"
:value-format="rangePickerProps.valueFormat"
:placeholder="rangePickerProps.placeholder"
:presets="rangePickerProps.presets"
class="!w-[235px]"
@change="handleDateRangeChange"
/>
<slot></slot>
</div>
</template>

View File

@@ -1,2 +0,0 @@
export { default as SummaryCard } from './summary-card.vue';
export type { SummaryCardProps } from './typing';

View File

@@ -1,57 +0,0 @@
<script lang="ts" setup>
import type { SummaryCardProps } from './typing';
import { CountTo } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { Tooltip } from 'ant-design-vue';
/** 统计卡片 */
defineOptions({ name: 'SummaryCard' });
defineProps<SummaryCardProps>();
</script>
<template>
<div
class="flex flex-row items-center gap-3 rounded bg-[var(--el-bg-color-overlay)] p-4"
>
<div
class="flex h-12 w-12 flex-shrink-0 items-center justify-center rounded"
:class="`${iconColor} ${iconBgColor}`"
>
<IconifyIcon v-if="icon" :icon="icon" class="!text-6" />
</div>
<div class="flex flex-col gap-1">
<div class="flex items-center gap-1">
<span class="text-base">{{ title }}</span>
<Tooltip :content="tooltip" placement="topLeft" v-if="tooltip">
<IconifyIcon
icon="lucide:circle-alert"
class="item-center !text-3 flex"
/>
</Tooltip>
</div>
<div class="flex flex-row items-baseline gap-2">
<div class="text-lg">
<CountTo
:prefix="prefix"
:end-val="value ?? 0"
:decimals="decimals ?? 0"
/>
</div>
<span
v-if="percent !== undefined"
:class="Number(percent) > 0 ? 'text-red-500' : 'text-green-500'"
>
<span class="text-sm">{{ Math.abs(Number(percent)) }}%</span>
<IconifyIcon
:icon="
Number(percent) > 0 ? 'lucide:chevron-up' : 'lucide:chevron-down'
"
class="ml-0.5 !text-sm"
/>
</span>
</div>
</div>
</div>
</template>

View File

@@ -1,11 +0,0 @@
export interface SummaryCardProps {
title: string;
tooltip?: string;
icon?: string;
iconColor?: string;
iconBgColor?: string;
prefix?: string;
value?: number;
decimals?: number;
percent?: number | string;
}

View File

@@ -10,4 +10,5 @@ export const ACTION_ICON = {
MORE: 'lucide:ellipsis-vertical', MORE: 'lucide:ellipsis-vertical',
VIEW: 'lucide:eye', VIEW: 'lucide:eye',
COPY: 'lucide:copy', COPY: 'lucide:copy',
CLOSE: 'lucide:x',
}; };

View File

@@ -241,7 +241,7 @@ function handleUploadSuccess(res: any, file: File) {
// 处理上传错误 // 处理上传错误
function handleUploadError(error: any) { function handleUploadError(error: any) {
console.error('上传错误:', error); console.error('上传错误:', error);
message.error($t('ui.upload.uploadError')); message.error('上传错误!!!');
// 上传失败时减少计数器 // 上传失败时减少计数器
uploadNumber.value = Math.max(0, uploadNumber.value - 1); uploadNumber.value = Math.max(0, uploadNumber.value - 1);
} }

View File

@@ -91,10 +91,12 @@ const components = [
FileUpload, FileUpload,
]; ];
// 参考 https://www.form-create.com/v3/ant-design-vue/auto-import 文档
export function setupFormCreate(app: App) { export function setupFormCreate(app: App) {
components.forEach((component) => { components.forEach((component) => {
app.component(component.name as string, component); app.component(component.name as string, component);
}); });
// TODO @xingyu这里为啥 app.component('AMessage', message); 看官方是没有的;
app.component('AMessage', message); app.component('AMessage', message);
formCreate.use(install); formCreate.use(install);
app.use(formCreate); app.use(formCreate);

View File

@@ -83,7 +83,7 @@ const routes: RouteRecordRaw[] = [
}, },
}, },
{ {
path: 'console/workflow/create', path: String.raw`workflow/create/:id(\d+)/:type(update|create)`,
component: () => import('#/views/ai/workflow/form/index.vue'), component: () => import('#/views/ai/workflow/form/index.vue'),
name: 'AiWorkflowCreate', name: 'AiWorkflowCreate',
meta: { meta: {

View File

@@ -51,7 +51,7 @@ const routes: RouteRecordRaw[] = [
name: 'BpmFormEditor', name: 'BpmFormEditor',
component: () => import('#/views/bpm/form/designer/index.vue'), component: () => import('#/views/bpm/form/designer/index.vue'),
meta: { meta: {
title: '编辑流程表单', title: '设计流程表单',
activePath: '/bpm/manager/form', activePath: '/bpm/manager/form',
}, },
props: (route) => { props: (route) => {

View File

@@ -18,7 +18,8 @@ const routes: RouteRecordRaw[] = [
title: '产品详情', title: '产品详情',
activePath: '/iot/device/product', activePath: '/iot/device/product',
}, },
component: () => import('#/views/iot/product/product/modules/detail/index.vue'), component: () =>
import('#/views/iot/product/product/modules/detail/index.vue'),
}, },
{ {
path: 'device/detail/:id', path: 'device/detail/:id',
@@ -27,11 +28,21 @@ const routes: RouteRecordRaw[] = [
title: '设备详情', title: '设备详情',
activePath: '/iot/device/device', activePath: '/iot/device/device',
}, },
component: () => import('#/views/iot/device/device/modules/detail/index.vue'), component: () =>
import('#/views/iot/device/device/modules/detail/index.vue'),
},
{
path: 'ota/firmware/detail/:id',
name: 'IoTOtaFirmwareDetail',
meta: {
title: '固件详情',
activePath: '/iot/ota',
},
component: () =>
import('#/views/iot/ota/modules/firmware-detail/index.vue'),
}, },
], ],
}, },
]; ];
export default routes; export default routes;

View File

@@ -18,7 +18,7 @@ const routes: RouteRecordRaw[] = [
title: '商品添加', title: '商品添加',
activePath: '/mall/product/spu', activePath: '/mall/product/spu',
}, },
component: () => import('#/views/mall/product/spu/modules/form.vue'), component: () => import('#/views/mall/product/spu/form/index.vue'),
}, },
{ {
path: String.raw`spu/edit/:id(\d+)`, path: String.raw`spu/edit/:id(\d+)`,
@@ -27,16 +27,16 @@ const routes: RouteRecordRaw[] = [
title: '商品编辑', title: '商品编辑',
activePath: '/mall/product/spu', activePath: '/mall/product/spu',
}, },
component: () => import('#/views/mall/product/spu/modules/form.vue'), component: () => import('#/views/mall/product/spu/form/index.vue'),
}, },
{ {
path: String.raw`spu/detail/:id(\d+)`, path: String.raw`spu/detail/:id(\d+)`,
name: 'ProductSpuDetail', name: 'ProductSpuDetail',
meta: { meta: {
title: '商品详情', title: '商品详情',
activePath: '/crm/business', activePath: '/mall/product/spu',
}, },
component: () => import('#/views/mall/product/spu/modules/detail.vue'), component: () => import('#/views/mall/product/spu/form/index.vue'),
}, },
], ],
}, },

View File

@@ -197,7 +197,7 @@ async function handleSubmit(values: Recordable<any>) {
await smsResetPassword({ mobile, code, password }); await smsResetPassword({ mobile, code, password });
message.success($t('authentication.resetPasswordSuccess')); message.success($t('authentication.resetPasswordSuccess'));
// 重置成功后跳转到首页 // 重置成功后跳转到首页
router.push('/'); await router.push('/');
} catch (error) { } catch (error) {
console.error('重置密码失败:', error); console.error('重置密码失败:', error);
} finally { } finally {

View File

@@ -11,7 +11,7 @@ import { authorize, getAuthorize } from '#/api/system/oauth2/open';
defineOptions({ name: 'SSOLogin' }); defineOptions({ name: 'SSOLogin' });
const { query } = useRoute(); // 路由参数 const { query } = useRoute();
const client = ref({ const client = ref({
name: '', name: '',

View File

@@ -17,6 +17,7 @@ import {
getChatConversationMyList, getChatConversationMyList,
updateChatConversationMy, updateChatConversationMy,
} from '#/api/ai/chat/conversation'; } from '#/api/ai/chat/conversation';
import { $t } from '#/locales';
import RoleRepository from '../role/RoleRepository.vue'; import RoleRepository from '../role/RoleRepository.vue';
@@ -201,7 +202,8 @@ async function updateConversationTitle(
if ( if (
filterConversationList.length > 0 && filterConversationList.length > 0 &&
filterConversationList[0] && // tip避免切换对话 filterConversationList[0] && // tip避免切换对话
activeConversationId.value === filterConversationList[0].id activeConversationId.value ===
(filterConversationList[0].id as number)
) { ) {
emits('onConversationClick', filterConversationList[0]); emits('onConversationClick', filterConversationList[0]);
} }
@@ -249,7 +251,7 @@ async function handleClearConversation() {
try { try {
await confirm('确认后对话会全部清空,置顶的对话除外。'); await confirm('确认后对话会全部清空,置顶的对话除外。');
await deleteChatConversationMyByUnpinned(); await deleteChatConversationMyByUnpinned();
message.success('操作成功!'); message.success($t('ui.actionMessage.operationSuccess'));
// 清空 对话 和 对话内容 // 清空 对话 和 对话内容
activeConversationId.value = null; activeConversationId.value = null;
// 获取 对话列表 // 获取 对话列表
@@ -305,7 +307,7 @@ onMounted(async () => {
<template> <template>
<Layout.Sider <Layout.Sider
width="280px" width="280px"
class="conversation-container relative flex h-full flex-col justify-between overflow-hidden p-4" class="relative flex h-full flex-col justify-between overflow-hidden p-4"
> >
<Drawer /> <Drawer />
<!-- 左顶部对话 --> <!-- 左顶部对话 -->
@@ -328,7 +330,7 @@ onMounted(async () => {
</Input> </Input>
<!-- 左中间对话列表 --> <!-- 左中间对话列表 -->
<div class="conversation-list mt-2 flex-1 overflow-auto"> <div class="mt-2 flex-1 overflow-auto">
<!-- 情况一加载中 --> <!-- 情况一加载中 -->
<Empty v-if="loading" description="." v-loading="loading" /> <Empty v-if="loading" description="." v-loading="loading" />
@@ -336,11 +338,10 @@ onMounted(async () => {
<div <div
v-for="conversationKey in Object.keys(conversationMap)" v-for="conversationKey in Object.keys(conversationMap)"
:key="conversationKey" :key="conversationKey"
class=""
> >
<div <div
v-if="conversationMap[conversationKey].length > 0" v-if="conversationMap[conversationKey].length > 0"
class="conversation-item classify-title pt-2" class="classify-title pt-2"
> >
<b class="mx-1"> <b class="mx-1">
{{ conversationKey }} {{ conversationKey }}
@@ -353,24 +354,24 @@ onMounted(async () => {
@click="handleConversationClick(conversation.id)" @click="handleConversationClick(conversation.id)"
@mouseover="hoverConversationId = conversation.id" @mouseover="hoverConversationId = conversation.id"
@mouseout="hoverConversationId = null" @mouseout="hoverConversationId = null"
class="conversation-item mt-1" class="mt-1"
> >
<div <div
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-lg px-2 leading-10" class="flex cursor-pointer flex-row items-center justify-between rounded-lg px-2 leading-10"
:class="[ :class="[
conversation.id === activeConversationId conversation.id === activeConversationId
? 'bg-primary-200' ? 'bg-success-600'
: '', : '',
]" ]"
> >
<div class="title-wrapper flex items-center"> <div class="flex items-center">
<Avatar <Avatar
v-if="conversation.roleAvatar" v-if="conversation.roleAvatar"
:src="conversation.roleAvatar" :src="conversation.roleAvatar"
/> />
<SvgGptIcon v-else class="size-8" /> <SvgGptIcon v-else class="size-8" />
<span <span
class="max-w-36 overflow-hidden text-ellipsis whitespace-nowrap p-2 text-sm font-normal text-gray-600" class="max-w-32 overflow-hidden text-ellipsis whitespace-nowrap p-2 text-sm font-normal"
> >
{{ conversation.title }} {{ conversation.title }}
</span> </span>
@@ -378,7 +379,7 @@ onMounted(async () => {
<div <div
v-show="hoverConversationId === conversation.id" v-show="hoverConversationId === conversation.id"
class="button-wrapper relative right-0.5 flex items-center text-gray-400" class="relative right-0.5 flex items-center text-gray-400"
> >
<Button <Button
class="mr-0 px-1" class="mr-0 px-1"

View File

@@ -17,7 +17,7 @@ import Form from '../../../../model/chatRole/modules/form.vue';
import RoleCategoryList from './RoleCategoryList.vue'; import RoleCategoryList from './RoleCategoryList.vue';
import RoleList from './RoleList.vue'; import RoleList from './RoleList.vue';
const router = useRouter(); // 路由对象 const router = useRouter();
const [Drawer] = useVbenDrawer({ const [Drawer] = useVbenDrawer({
title: '角色管理', title: '角色管理',
footer: false, footer: false,

View File

@@ -28,7 +28,7 @@ import MessageNewConversation from './components/message/MessageNewConversation.
/** AI 聊天对话 列表 */ /** AI 聊天对话 列表 */
defineOptions({ name: 'AiChat' }); defineOptions({ name: 'AiChat' });
const route = useRoute(); // 路由 const route = useRoute();
const [FormModal, formModalApi] = useVbenModal({ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: ConversationUpdateForm, connectedComponent: ConversationUpdateForm,
destroyOnClose: true, destroyOnClose: true,
@@ -428,7 +428,7 @@ async function textRoll() {
// 设置状态 // 设置状态
textRoleRunning.value = true; textRoleRunning.value = true;
receiveMessageDisplayedText.value = ''; receiveMessageDisplayedText.value = '';
const task = async () => { async function task() {
// 调整速度 // 调整速度
const diff = const diff =
(receiveMessageFullText.value.length - (receiveMessageFullText.value.length -
@@ -472,7 +472,7 @@ async function textRoll() {
clearTimeout(timer); clearTimeout(timer);
} }
} }
}; }
let timer = setTimeout(task, textSpeed.value); let timer = setTimeout(task, textSpeed.value);
} catch {} } catch {}
} }
@@ -503,7 +503,7 @@ onMounted(async () => {
<!-- 左侧对话列表 --> <!-- 左侧对话列表 -->
<ConversationList <ConversationList
class="!bg-card" class="!bg-card"
:active-id="activeConversationId as any" :active-id="activeConversationId"
ref="conversationListRef" ref="conversationListRef"
@on-conversation-create="handleConversationCreateSuccess" @on-conversation-create="handleConversationCreateSuccess"
@on-conversation-click="handleConversationClick" @on-conversation-click="handleConversationClick"
@@ -578,7 +578,7 @@ onMounted(async () => {
class="border-border my-5 mb-5 mt-2 flex flex-col rounded-xl border px-2 py-2.5" class="border-border my-5 mb-5 mt-2 flex flex-col rounded-xl border px-2 py-2.5"
> >
<textarea <textarea
class="box-border h-24 resize-none overflow-auto border-none px-0 py-1 focus:outline-none" class="box-border h-24 resize-none overflow-auto rounded-md px-0 py-1 focus:outline-none"
v-model="prompt" v-model="prompt"
@keydown="handleSendByKeydown" @keydown="handleSendByKeydown"
@input="handlePromptInput" @input="handlePromptInput"

View File

@@ -84,9 +84,9 @@ onMounted(async () => {
<TableAction :actions="[]" /> <TableAction :actions="[]" />
</template> </template>
<template #userId="{ row }"> <template #userId="{ row }">
<span>{{ <span>
userList.find((item) => item.id === row.userId)?.nickname {{ userList.find((item) => item.id === row.userId)?.nickname }}
}}</span> </span>
</template> </template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction

View File

@@ -44,7 +44,7 @@ watch(
<template> <template>
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="mt-2 text-gray-600"> <div class="mt-2">
<Image class="rounded-lg" :src="detail?.picUrl" /> <Image class="rounded-lg" :src="detail?.picUrl" />
</div> </div>
</div> </div>
@@ -52,7 +52,7 @@ watch(
<!-- 时间 --> <!-- 时间 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">时间</div> <div class="text-lg font-bold">时间</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
<div> <div>
提交时间{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }} 提交时间{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
</div> </div>
@@ -65,7 +65,7 @@ watch(
<!-- 模型 --> <!-- 模型 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">模型</div> <div class="text-lg font-bold">模型</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.model }}({{ detail.height }}x{{ detail.width }}) {{ detail.model }}({{ detail.height }}x{{ detail.width }})
</div> </div>
</div> </div>
@@ -73,7 +73,7 @@ watch(
<!-- 提示词 --> <!-- 提示词 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">提示词</div> <div class="text-lg font-bold">提示词</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.prompt }} {{ detail.prompt }}
</div> </div>
</div> </div>
@@ -81,7 +81,7 @@ watch(
<!-- 图片地址 --> <!-- 图片地址 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">图片地址</div> <div class="text-lg font-bold">图片地址</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.picUrl }} {{ detail.picUrl }}
</div> </div>
</div> </div>
@@ -95,7 +95,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">采样方法</div> <div class="text-lg font-bold">采样方法</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionSamplers.find( StableDiffusionSamplers.find(
(item) => item.key === detail?.options?.sampler, (item) => item.key === detail?.options?.sampler,
@@ -112,7 +112,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">CLIP</div> <div class="text-lg font-bold">CLIP</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionClipGuidancePresets.find( StableDiffusionClipGuidancePresets.find(
(item) => item.key === detail?.options?.clipGuidancePreset, (item) => item.key === detail?.options?.clipGuidancePreset,
@@ -129,7 +129,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">风格</div> <div class="text-lg font-bold">风格</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionStylePresets.find( StableDiffusionStylePresets.find(
(item) => item.key === detail?.options?.stylePreset, (item) => item.key === detail?.options?.stylePreset,
@@ -146,7 +146,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">迭代步数</div> <div class="text-lg font-bold">迭代步数</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.steps }}</div> <div class="mt-2">{{ detail?.options?.steps }}</div>
</div> </div>
<div <div
@@ -157,7 +157,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">引导系数</div> <div class="text-lg font-bold">引导系数</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.scale }}</div> <div class="mt-2">{{ detail?.options?.scale }}</div>
</div> </div>
<div <div
@@ -168,7 +168,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">随机因子</div> <div class="text-lg font-bold">随机因子</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.seed }}</div> <div class="mt-2">{{ detail?.options?.seed }}</div>
</div> </div>
<!-- Dall3 专属 --> <!-- Dall3 专属 -->
@@ -177,7 +177,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">风格选择</div> <div class="text-lg font-bold">风格选择</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name
}} }}
@@ -192,7 +192,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">模型版本</div> <div class="text-lg font-bold">模型版本</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.version }}</div> <div class="mt-2">{{ detail?.options?.version }}</div>
</div> </div>
<div <div
@@ -203,7 +203,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">参考图</div> <div class="text-lg font-bold">参考图</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
<Image :src="detail.options.referImageUrl" /> <Image :src="detail.options.referImageUrl" />
</div> </div>
</div> </div>

View File

@@ -24,7 +24,7 @@ import ImageDetail from './ImageDetail.vue';
// 暴露组件方法 // 暴露组件方法
const emits = defineEmits(['onRegeneration']); const emits = defineEmits(['onRegeneration']);
const router = useRouter(); // 路由 const router = useRouter();
const [Drawer, drawerApi] = useVbenDrawer({ const [Drawer, drawerApi] = useVbenDrawer({
title: '图片详情', title: '图片详情',
footer: false, footer: false,

View File

@@ -47,15 +47,15 @@ const platformOptions = [
const models = ref<AiModelModelApi.Model[]>([]); // 模型列表 const models = ref<AiModelModelApi.Model[]>([]); // 模型列表
/** 绘画 start */ /** 绘画 start */
const handleDrawStart = async () => {}; async function handleDrawStart() {}
/** 绘画 complete */ /** 绘画 complete */
const handleDrawComplete = async () => { async function handleDrawComplete() {
await imageListRef.value.getImageList(); await imageListRef.value.getImageList();
}; }
/** 重新生成:将画图详情填充到对应平台 */ /** 重新生成:将画图详情填充到对应平台 */
const handleRegeneration = async (image: AiImageApi.Image) => { async function handleRegeneration(image: AiImageApi.Image) {
// 切换平台 // 切换平台
selectPlatform.value = image.platform; selectPlatform.value = image.platform;
// 根据不同平台填充 image // 根据不同平台填充 image
@@ -79,7 +79,7 @@ const handleRegeneration = async (image: AiImageApi.Image) => {
// No default // No default
} }
// TODO @fan貌似 other 重新设置不行? // TODO @fan貌似 other 重新设置不行?
}; }
/** 组件挂载的时候 */ /** 组件挂载的时候 */
onMounted(async () => { onMounted(async () => {

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -69,15 +77,20 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
fixed: 'left', fixed: 'left',
}, },
{ {
field: 'picUrl',
title: '图片', title: '图片',
minWidth: 110, minWidth: 110,
fixed: 'left', fixed: 'left',
slots: { default: 'picUrl' }, cellRender: {
name: 'CellImage',
},
}, },
{ {
minWidth: 180, field: 'userId',
title: '用户', title: '用户',
slots: { default: 'userId' }, minWidth: 180,
formatter: ({ cellValue }) =>
userList.find((user) => user.id === cellValue)?.nickname || '-',
}, },
{ {
field: 'platform', field: 'platform',

View File

@@ -1,23 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiImageApi } from '#/api/ai/image'; import type { AiImageApi } from '#/api/ai/image';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { confirm, DocAlert, Page } from '@vben/common-ui'; import { confirm, DocAlert, Page } from '@vben/common-ui';
import { AiImageStatusEnum } from '@vben/constants'; import { AiImageStatusEnum } from '@vben/constants';
import { Image, message, Switch } from 'ant-design-vue'; import { message, Switch } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteImage, getImagePage, updateImage } from '#/api/ai/image'; import { deleteImage, getImagePage, updateImage } from '#/api/ai/image';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -40,7 +35,7 @@ async function handleDelete(row: AiImageApi.Image) {
} }
} }
/** 修改是否发布 */ /** 修改是否发布 */
const handleUpdatePublicStatusChange = async (row: AiImageApi.Image) => { async function handleUpdatePublicStatusChange(row: AiImageApi.Image) {
try { try {
// 修改状态的二次确认 // 修改状态的二次确认
const text = row.publicStatus ? '公开' : '私有'; const text = row.publicStatus ? '公开' : '私有';
@@ -54,7 +49,7 @@ const handleUpdatePublicStatusChange = async (row: AiImageApi.Image) => {
} catch { } catch {
row.publicStatus = !row.publicStatus; row.publicStatus = !row.publicStatus;
} }
}; }
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiImageApi.Image>, } as VxeTableGridOptions<AiImageApi.Image>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -95,20 +86,6 @@ onMounted(async () => {
<DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" /> <DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" />
</template> </template>
<Grid table-title="绘画管理列表"> <Grid table-title="绘画管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #picUrl="{ row }">
<Image :src="row.picUrl" class="h-20 w-20" />
</template>
<template #userId="{ row }">
<span>
{{
userList.find((item: SystemUserApi.User) => item.id === row.userId)
?.nickname
}}
</span>
</template>
<template #publicStatus="{ row }"> <template #publicStatus="{ row }">
<Switch <Switch
v-model:checked="row.publicStatus" v-model:checked="row.publicStatus"

View File

@@ -33,10 +33,10 @@ async function getList() {
} }
const debounceGetList = useDebounceFn(getList, 80); const debounceGetList = useDebounceFn(getList, 80);
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { function handleQuery() {
queryParams.pageNo = 1; queryParams.pageNo = 1;
getList(); getList();
}; }
/** 初始化 */ /** 初始化 */
onMounted(async () => { onMounted(async () => {

View File

@@ -20,8 +20,8 @@ import ProcessStep from './ProcessStep.vue';
import SplitStep from './SplitStep.vue'; import SplitStep from './SplitStep.vue';
import UploadStep from './UploadStep.vue'; import UploadStep from './UploadStep.vue';
const route = useRoute(); // 路由 const route = useRoute();
const router = useRouter(); // 路由 const router = useRouter();
// 组件引用 // 组件引用
const uploadDocumentRef = ref(); const uploadDocumentRef = ref();

View File

@@ -25,8 +25,8 @@ import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'AiKnowledgeDocument' }); defineOptions({ name: 'AiKnowledgeDocument' });
const { hasAccessByCodes } = useAccess(); const { hasAccessByCodes } = useAccess();
const route = useRoute(); // 路由 const route = useRoute();
const router = useRouter(); // 路由 const router = useRouter();
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -65,16 +65,16 @@ async function handleDelete(row: AiKnowledgeDocumentApi.KnowledgeDocument) {
} }
} }
/** 跳转到知识库分段页面 */ /** 跳转到知识库分段页面 */
const handleSegment = (id: number) => { function handleSegment(id: number) {
router.push({ router.push({
name: 'AiKnowledgeSegment', name: 'AiKnowledgeSegment',
query: { documentId: id }, query: { documentId: id },
}); });
}; }
/** 修改是否发布 */ /** 修改是否发布 */
const handleStatusChange = async ( async function handleStatusChange(
row: AiKnowledgeDocumentApi.KnowledgeDocument, row: AiKnowledgeDocumentApi.KnowledgeDocument,
) => { ) {
try { try {
// 修改状态的二次确认 // 修改状态的二次确认
const text = row.status ? '启用' : '禁用'; const text = row.status ? '启用' : '禁用';
@@ -91,7 +91,7 @@ const handleStatusChange = async (
? CommonStatusEnum.DISABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE; : CommonStatusEnum.ENABLE;
} }
}; }
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),

View File

@@ -20,8 +20,8 @@ import { searchKnowledgeSegment } from '#/api/ai/knowledge/segment';
/** 文档召回测试 */ /** 文档召回测试 */
defineOptions({ name: 'KnowledgeDocumentRetrieval' }); defineOptions({ name: 'KnowledgeDocumentRetrieval' });
const route = useRoute(); // 路由 const route = useRoute();
const router = useRouter(); // 路由 const router = useRouter();
const loading = ref(false); // 加载状态 const loading = ref(false); // 加载状态
const segments = ref<any[]>([]); // 召回结果 const segments = ref<any[]>([]); // 召回结果

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiKnowledgeKnowledgeApi } from '#/api/ai/knowledge/knowledge';
import type { AiKnowledgeSegmentApi } from '#/api/ai/knowledge/segment'; import type { AiKnowledgeSegmentApi } from '#/api/ai/knowledge/segment';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
@@ -41,12 +40,12 @@ function handleCreate() {
} }
/** 编辑 */ /** 编辑 */
function handleEdit(row: AiKnowledgeKnowledgeApi.Knowledge) { function handleEdit(row: AiKnowledgeSegmentApi.KnowledgeSegment) {
formModalApi.setData(row).open(); formModalApi.setData(row).open();
} }
/** 删除 */ /** 删除 */
async function handleDelete(row: AiKnowledgeKnowledgeApi.Knowledge) { async function handleDelete(row: AiKnowledgeSegmentApi.KnowledgeSegment) {
const hideLoading = message.loading({ const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.id]), content: $t('ui.actionMessage.deleting', [row.id]),
duration: 0, duration: 0,
@@ -88,7 +87,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: true, refresh: true,
search: true, search: true,
}, },
} as VxeTableGridOptions<AiKnowledgeKnowledgeApi.Knowledge>, } as VxeTableGridOptions<AiKnowledgeSegmentApi.KnowledgeSegment>,
}); });
/** 修改是否发布 */ /** 修改是否发布 */

View File

@@ -27,7 +27,7 @@ defineExpose({
<h3 class="text-primary h-7 w-full text-center text-xl leading-7"> <h3 class="text-primary h-7 w-full text-center text-xl leading-7">
思维导图创作中心 思维导图创作中心
</h3> </h3>
<div class="flex-grow overflow-y-auto"> <div class="mt-4 flex-grow overflow-y-auto">
<div> <div>
<b>您的需求</b> <b>您的需求</b>
<Textarea <Textarea

View File

@@ -73,7 +73,7 @@ watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => {
}); });
/** 更新思维导图的展示 */ /** 更新思维导图的展示 */
const update = () => { function update() {
try { try {
const { root } = transformer.transform( const { root } = transformer.transform(
processContent(props.generatedContent), processContent(props.generatedContent),
@@ -83,7 +83,7 @@ const update = () => {
} catch (error: any) { } catch (error: any) {
console.error(error); console.error(error);
} }
}; }
/** 处理内容 */ /** 处理内容 */
function processContent(text: string) { function processContent(text: string) {
const arr: string[] = []; const arr: string[] = [];

View File

@@ -1,9 +1,17 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -44,9 +52,11 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
fixed: 'left', fixed: 'left',
}, },
{ {
minWidth: 180, field: 'userId',
title: '用户', title: '用户',
slots: { default: 'userId' }, minWidth: 180,
formatter: ({ cellValue }) =>
userList.find((user) => user.id === cellValue)?.nickname || '-',
}, },
{ {
field: 'prompt', field: 'prompt',

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiMindmapApi } from '#/api/ai/mindmap'; import type { AiMindmapApi } from '#/api/ai/mindmap';
import type { SystemUserApi } from '#/api/system/user';
import { nextTick, onMounted, ref } from 'vue'; import { nextTick, ref } from 'vue';
import { DocAlert, Page, useVbenDrawer } from '@vben/common-ui'; import { DocAlert, Page, useVbenDrawer } from '@vben/common-ui';
@@ -11,14 +10,11 @@ import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMindMap, getMindMapPage } from '#/api/ai/mindmap'; import { deleteMindMap, getMindMapPage } from '#/api/ai/mindmap';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import Right from '../index/modules/Right.vue'; import Right from '../index/modules/Right.vue';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
const previewVisible = ref(false); // drawer 的显示隐藏
const previewContent = ref(''); const previewContent = ref('');
const [Drawer, drawerApi] = useVbenDrawer({ const [Drawer, drawerApi] = useVbenDrawer({
header: false, header: false,
@@ -75,16 +71,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
} as VxeTableGridOptions<AiMindmapApi.MindMap>, } as VxeTableGridOptions<AiMindmapApi.MindMap>,
}); });
async function openPreview(row: AiMindmapApi.MindMap) { async function openPreview(row: AiMindmapApi.MindMap) {
previewVisible.value = false; previewContent.value = row.generatedContent;
drawerApi.open(); drawerApi.open();
await nextTick(); await nextTick();
previewVisible.value = true;
previewContent.value = row.generatedContent;
} }
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -94,7 +84,6 @@ onMounted(async () => {
</template> </template>
<Drawer class="w-3/5"> <Drawer class="w-3/5">
<Right <Right
v-if="previewVisible"
:generated-content="previewContent" :generated-content="previewContent"
:is-end="true" :is-end="true"
:is-generating="false" :is-generating="false"
@@ -102,17 +91,6 @@ onMounted(async () => {
/> />
</Drawer> </Drawer>
<Grid table-title="思维导图管理列表"> <Grid table-title="思维导图管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #userId="{ row }">
<span>
{{
userList.find((item: SystemUserApi.User) => item.id === row.userId)
?.nickname
}}
</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -209,8 +209,15 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: '角色头像', title: '角色头像',
slots: { default: 'avatar' }, field: 'avatar',
minWidth: 140, minWidth: 140,
cellRender: {
name: 'CellImage',
props: {
width: 40,
height: 40,
},
},
}, },
{ {
title: '角色类别', title: '角色类别',
@@ -229,13 +236,23 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: '知识库', title: '知识库',
slots: { default: 'knowledgeIds' }, field: 'knowledgeIds',
minWidth: 100, minWidth: 100,
formatter: ({ cellValue }) => {
return !cellValue || cellValue.length === 0
? '-'
: `引用${cellValue.length}`;
},
}, },
{ {
title: '工具', title: '工具',
slots: { default: 'toolIds' }, field: 'toolIds',
minWidth: 100, minWidth: 100,
formatter: ({ cellValue }) => {
return !cellValue || cellValue.length === 0
? '-'
: `引用${cellValue.length}`;
},
}, },
{ {
field: 'publicStatus', field: 'publicStatus',

View File

@@ -4,7 +4,7 @@ import type { AiModelChatRoleApi } from '#/api/ai/model/chatRole';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { Image, message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteChatRole, getChatRolePage } from '#/api/ai/model/chatRole'; import { deleteChatRole, getChatRolePage } from '#/api/ai/model/chatRole';
@@ -100,17 +100,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
]" ]"
/> />
</template> </template>
<template #knowledgeIds="{ row }">
<span v-if="!row.knowledgeIds || row.knowledgeIds.length === 0">-</span>
<span v-else>引用 {{ row.knowledgeIds.length }} </span>
</template>
<template #toolIds="{ row }">
<span v-if="!row.toolIds || row.toolIds.length === 0">-</span>
<span v-else>引用 {{ row.toolIds.length }} </span>
</template>
<template #avatar="{ row }">
<Image :src="row.avatar" class="h-8 w-8" />
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -1,11 +1,20 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiModelApiKeyApi } from '#/api/ai/model/apiKey';
import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { getApiKeySimpleList } from '#/api/ai/model/apiKey'; import { getApiKeySimpleList } from '#/api/ai/model/apiKey';
let apiKeyList: AiModelApiKeyApi.ApiKey[] = [];
async function getApiKeyList() {
apiKeyList = await getApiKeySimpleList();
}
getApiKeyList();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -199,7 +208,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: 'API 秘钥', title: 'API 秘钥',
slots: { default: 'keyId' }, field: 'keyId',
formatter: ({ cellValue }) => {
return (
apiKeyList.find((apiKey) => apiKey.id === cellValue)?.name || '-'
);
},
minWidth: 140, minWidth: 140,
}, },
{ {

View File

@@ -1,23 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiModelApiKeyApi } from '#/api/ai/model/apiKey';
import type { AiModelModelApi } from '#/api/ai/model/model'; import type { AiModelModelApi } from '#/api/ai/model/model';
import { onMounted, ref } from 'vue';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getApiKeySimpleList } from '#/api/ai/model/apiKey';
import { deleteModel, getModelPage } from '#/api/ai/model/model'; import { deleteModel, getModelPage } from '#/api/ai/model/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue'; import Form from './modules/form.vue';
const apiKeyList = ref([] as AiModelApiKeyApi.ApiKey[]);
const [FormModal, formModalApi] = useVbenModal({ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form, connectedComponent: Form,
destroyOnClose: true, destroyOnClose: true,
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiModelModelApi.Model>, } as VxeTableGridOptions<AiModelModelApi.Model>,
}); });
onMounted(async () => {
// 获得下拉数据
apiKeyList.value = await getApiKeySimpleList();
});
</script> </script>
<template> <template>
@@ -109,15 +100,6 @@ onMounted(async () => {
]" ]"
/> />
</template> </template>
<template #keyId="{ row }">
<span>
{{
apiKeyList.find(
(item: AiModelApiKeyApi.ApiKey) => item.id === row.keyId,
)?.name
}}
</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -82,7 +90,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
minWidth: 180, minWidth: 180,
title: '用户', title: '用户',
slots: { default: 'userId' }, field: 'userId',
formatter: ({ cellValue }) => {
return userList.find((user) => user.id === cellValue)?.nickname || '-';
},
}, },
{ {
field: 'status', field: 'status',

View File

@@ -1,9 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiMusicApi } from '#/api/ai/music'; import type { AiMusicApi } from '#/api/ai/music';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { confirm, DocAlert, Page } from '@vben/common-ui'; import { confirm, DocAlert, Page } from '@vben/common-ui';
import { AiMusicStatusEnum } from '@vben/constants'; import { AiMusicStatusEnum } from '@vben/constants';
@@ -12,12 +9,10 @@ import { Button, message, Switch } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music'; import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiMusicApi.Music>, } as VxeTableGridOptions<AiMusicApi.Music>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -99,11 +90,6 @@ onMounted(async () => {
<TableAction :actions="[]" /> <TableAction :actions="[]" />
</template> </template>
<template #userId="{ row }">
<span>
{{ userList.find((item) => item.id === row.userId)?.nickname }}
</span>
</template>
<template #content="{ row }"> <template #content="{ row }">
<Button <Button
type="link" type="link"

View File

@@ -118,7 +118,7 @@ async function handleSave() {
// 保存成功,提示并跳转到列表页 // 保存成功,提示并跳转到列表页
message.success('保存成功'); message.success('保存成功');
tabs.closeCurrentTab(); await tabs.closeCurrentTab();
await router.push({ name: 'AiWorkflow' }); await router.push({ name: 'AiWorkflow' });
} catch (error: any) { } catch (error: any) {
console.error('保存失败:', error); console.error('保存失败:', error);
@@ -152,8 +152,8 @@ async function handleDeploy() {
// 发布 // 发布
await deployModel(formData.value.id); await deployModel(formData.value.id);
message.success('发布成功'); message.success('发布成功');
// TODO 返回列表页 // 返回列表页
await router.push({ name: '/ai/workflow' }); await router.push({ name: 'AiWorkflow' });
} catch (error: any) { } catch (error: any) {
console.error('发布失败:', error); console.error('发布失败:', error);
message.warning(error.message || '发布失败'); message.warning(error.message || '发布失败');
@@ -190,8 +190,8 @@ function handleBack() {
/** 初始化 */ /** 初始化 */
onMounted(async () => { onMounted(async () => {
workflowId.value = route.query.id as string; workflowId.value = route.params.id as string;
actionType.value = route.query.type as string; actionType.value = route.params.type as string;
await initData(); await initData();
}); });

View File

@@ -245,7 +245,7 @@ defineExpose({ validate });
</fieldset> </fieldset>
<fieldset <fieldset
class="m-0 mt-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-4" class="bg-card m-0 mt-10 rounded-lg border border-gray-200 px-3 py-4"
> >
<legend class="ml-2 px-2.5 text-base font-semibold text-gray-600"> <legend class="ml-2 px-2.5 text-base font-semibold text-gray-600">
<h3>运行结果</h3> <h3>运行结果</h3>

View File

@@ -28,10 +28,7 @@ function handleCreate() {
function handleEdit(row: any) { function handleEdit(row: any) {
router.push({ router.push({
name: 'AiWorkflowCreate', name: 'AiWorkflowCreate',
query: { params: { id: row.id, type: 'update' },
id: row.id,
type: 'update',
},
}); });
} }

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -62,7 +70,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
minWidth: 180, minWidth: 180,
title: '用户', title: '用户',
slots: { default: 'userId' }, field: 'userId',
formatter: ({ cellValue }) => {
return userList.find((user) => user.id === cellValue)?.nickname || '-';
},
}, },
{ {
field: 'type', field: 'type',

View File

@@ -1,9 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiWriteApi } from '#/api/ai/write'; import type { AiWriteApi } from '#/api/ai/write';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { DocAlert, Page } from '@vben/common-ui'; import { DocAlert, Page } from '@vben/common-ui';
@@ -11,12 +8,10 @@ import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteWrite, getWritePage } from '#/api/ai/write'; import { deleteWrite, getWritePage } from '#/api/ai/write';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -66,10 +61,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiWriteApi.AiWritePageReq>, } as VxeTableGridOptions<AiWriteApi.AiWritePageReq>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -78,14 +69,6 @@ onMounted(async () => {
<DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" /> <DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" />
</template> </template>
<Grid table-title="写作管理列表"> <Grid table-title="写作管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #userId="{ row }">
<span>{{
userList.find((item) => item.id === row.userId)?.nickname
}}</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -62,12 +62,26 @@ export function useFormSchema(): VbenFormSchema[] {
componentProps: { componentProps: {
min: 0, min: 0,
placeholder: '请输入分类排序', placeholder: '请输入分类排序',
class: 'w-full',
}, },
}, },
]; ];
} }
/** 重命名的表单 */
export function useRenameFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'name',
label: '分类名',
component: 'Input',
componentProps: {
placeholder: '请输入分类名',
},
rules: 'required',
},
];
}
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -143,6 +157,11 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
props: { type: DICT_TYPE.COMMON_STATUS }, props: { type: DICT_TYPE.COMMON_STATUS },
}, },
}, },
{
field: 'sort',
title: '分类排序',
minWidth: 100,
},
{ {
field: 'createTime', field: 'createTime',
title: '创建时间', title: '创建时间',

View File

@@ -36,16 +36,14 @@ function handleEdit(row: BpmCategoryApi.Category) {
/** 删除流程分类 */ /** 删除流程分类 */
async function handleDelete(row: BpmCategoryApi.Category) { async function handleDelete(row: BpmCategoryApi.Category) {
const hideLoading = message.loading({ const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.code]), content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0, duration: 0,
}); });
try { try {
await deleteCategory(row.id as number); await deleteCategory(row.id as number);
message.success({ message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
content: $t('ui.actionMessage.deleteSuccess', [row.code]),
});
handleRefresh(); handleRefresh();
} catch { } finally {
hideLoading(); hideLoading();
} }
} }
@@ -71,6 +69,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
rowConfig: { rowConfig: {
keyField: 'id', keyField: 'id',
isHover: true,
}, },
toolbarConfig: { toolbarConfig: {
refresh: true, refresh: true,

View File

@@ -26,6 +26,13 @@ const getTitle = computed(() => {
}); });
const [Form, formApi] = useVbenForm({ const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
layout: 'horizontal', layout: 'horizontal',
schema: useFormSchema(), schema: useFormSchema(),
showDefaultActions: false, showDefaultActions: false,
@@ -73,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :title="getTitle"> <Modal :title="getTitle" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@@ -11,50 +11,36 @@ import { useVbenForm } from '#/adapter/form';
import { getCategory, updateCategory } from '#/api/bpm/category'; import { getCategory, updateCategory } from '#/api/bpm/category';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useRenameFormSchema } from '../data';
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
const formData = ref<BpmCategoryApi.Category>(); const formData = ref<BpmCategoryApi.Category>();
// 定义表单结构
const formSchema = [
{
fieldName: 'name',
label: '分类名',
component: 'Input',
componentProps: {
placeholder: '请输入分类名',
},
rules: 'required',
},
];
// 创建表单
const [Form, formApi] = useVbenForm({ const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
layout: 'horizontal', layout: 'horizontal',
schema: formSchema, schema: useRenameFormSchema(),
showDefaultActions: false, showDefaultActions: false,
}); });
// 创建模态窗
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
// 保存按钮回调
async onConfirm() { async onConfirm() {
const { valid } = await formApi.validate(); const { valid } = await formApi.validate();
if (!valid) { if (!valid) {
return; return;
} }
modalApi.lock(); modalApi.lock();
// 提交表单
// 提交表单,只更新流程分类名
const formValues = await formApi.getValues();
const data = { const data = {
id: formData.value?.id, ...formData.value,
name: formValues.name, // 只更新流程分类名 ...(await formApi.getValues()),
code: formData.value?.code,
status: formData.value?.status,
description: formData.value?.description,
sort: formData.value?.sort,
} as BpmCategoryApi.Category; } as BpmCategoryApi.Category;
try { try {
await updateCategory(data); await updateCategory(data);
// 关闭并提示 // 关闭并提示
@@ -65,29 +51,21 @@ const [Modal, modalApi] = useVbenModal({
modalApi.unlock(); modalApi.unlock();
} }
}, },
// 打开/关闭弹窗回调
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
formData.value = undefined; formData.value = undefined;
return; return;
} }
// 加载数据 // 加载数据
const data = modalApi.getData<BpmCategoryApi.Category>(); const data = modalApi.getData<BpmCategoryApi.Category>();
if (!data || !data.id) { if (!data || !data.id) {
return; return;
} }
modalApi.lock(); modalApi.lock();
try { try {
// 获取流程分类数据
formData.value = await getCategory(data.id); formData.value = await getCategory(data.id);
// 设置 name 字段 // 设置到 values
await formApi.setValues({ await formApi.setValues(formData.value);
name: formData.value.name,
});
} finally { } finally {
modalApi.unlock(); modalApi.unlock();
} }
@@ -96,7 +74,7 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal title="重命名流程分类"> <Modal title="重命名流程分类" class="w-1/3">
<Form class="mx-4" /> <Form class="mx-4" />
</Modal> </Modal>
</template> </template>

View File

@@ -0,0 +1,2 @@
// 导出 BPMN 流程设计器相关组件
export * from './package';

View File

@@ -740,7 +740,10 @@
"name": "FailedJobRetryTimeCycle", "name": "FailedJobRetryTimeCycle",
"superClass": ["Element"], "superClass": ["Element"],
"meta": { "meta": {
"allowedIn": ["activiti:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] "allowedIn": [
"activiti:AsyncCapable",
"bpmn:MultiInstanceLoopCharacteristics"
]
}, },
"properties": [ "properties": [
{ {

View File

@@ -727,7 +727,10 @@
"name": "FailedJobRetryTimeCycle", "name": "FailedJobRetryTimeCycle",
"superClass": ["Element"], "superClass": ["Element"],
"meta": { "meta": {
"allowedIn": ["camunda:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] "allowedIn": [
"camunda:AsyncCapable",
"bpmn:MultiInstanceLoopCharacteristics"
]
}, },
"properties": [ "properties": [
{ {

View File

@@ -910,7 +910,10 @@
"name": "FailedJobRetryTimeCycle", "name": "FailedJobRetryTimeCycle",
"superClass": ["Element"], "superClass": ["Element"],
"meta": { "meta": {
"allowedIn": ["flowable:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] "allowedIn": [
"flowable:AsyncCapable",
"bpmn:MultiInstanceLoopCharacteristics"
]
}, },
"properties": [ "properties": [
{ {
@@ -1254,11 +1257,11 @@
"allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
}, },
"properties": [ "properties": [
{ {
"name": "value", "name": "value",
"type": "Integer", "type": "Integer",
"isBody": true "isBody": true
} }
] ]
}, },
{ {

View File

@@ -0,0 +1,13 @@
import './theme/index.scss';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
// TODO @puhui999样式问题设计器那位置不太对
export { default as MyProcessDesigner } from './designer';
// TODO @puhui999流程发起时预览相关的需要使用
export { default as MyProcessViewer } from './designer/index2';
export { default as MyProcessPenal } from './penal';
// TODO @jason【有个迁移的打印】【新增】流程打印由 [@Lesan](https://gitee.com/LesanOuO) 贡献 [#816](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/816/)、[#1418](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1418/)、[#817](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/817/)、[#1419](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1419/)、[#1424](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1424)、[#819](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/819)、[#821](https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/821/)

View File

@@ -6,8 +6,8 @@ import { cloneDeep } from '@vben/utils';
import { Collapse } from 'ant-design-vue'; import { Collapse } from 'ant-design-vue';
import ElementCustomConfig from '#/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue'; import ElementCustomConfig from '#/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue';
import ElementForm from '#/components/bpmn-process-designer/package/penal/form/ElementForm.vue'; import ElementForm from '#/views/bpm/components/bpmn-process-designer/package/penal/form/ElementForm.vue';
import ElementBaseInfo from './base/ElementBaseInfo.vue'; import ElementBaseInfo from './base/ElementBaseInfo.vue';
import FlowCondition from './flow-condition/FlowCondition.vue'; import FlowCondition from './flow-condition/FlowCondition.vue';

View File

@@ -12,12 +12,12 @@ import {
Switch, Switch,
} from 'ant-design-vue'; } from 'ant-design-vue';
import { convertTimeUnit } from '#/components/simple-process-design/components/nodes-config/utils'; import { convertTimeUnit } from '#/views/bpm/components/simple-process-design/components/nodes-config/utils';
import { import {
TIME_UNIT_TYPES, TIME_UNIT_TYPES,
TIMEOUT_HANDLER_TYPES, TIMEOUT_HANDLER_TYPES,
TimeUnitType, TimeUnitType,
} from '#/components/simple-process-design/consts'; } from '#/views/bpm/components/simple-process-design/consts';
defineOptions({ name: 'ElementCustomConfig4BoundaryEventTimer' }); defineOptions({ name: 'ElementCustomConfig4BoundaryEventTimer' });
const props = defineProps({ const props = defineProps({

View File

@@ -9,7 +9,7 @@
--> -->
<script lang="ts" setup> <script lang="ts" setup>
import type { SystemUserApi } from '#/api/system/user'; import type { SystemUserApi } from '#/api/system/user';
import type { ButtonSetting } from '#/components/simple-process-design/consts'; import type { ButtonSetting } from '#/views/bpm/components/simple-process-design/consts';
import { inject, nextTick, onMounted, ref, toRaw, watch } from 'vue'; import { inject, nextTick, onMounted, ref, toRaw, watch } from 'vue';
@@ -38,8 +38,8 @@ import {
OPERATION_BUTTON_NAME, OPERATION_BUTTON_NAME,
REJECT_HANDLER_TYPES, REJECT_HANDLER_TYPES,
RejectHandlerType, RejectHandlerType,
} from '#/components/simple-process-design/consts'; } from '#/views/bpm/components/simple-process-design/consts';
import { useFormFieldsPermission } from '#/components/simple-process-design/helpers'; import { useFormFieldsPermission } from '#/views/bpm/components/simple-process-design/helpers';
defineOptions({ name: 'ElementCustomConfig4UserTask' }); defineOptions({ name: 'ElementCustomConfig4UserTask' });
const props = defineProps({ const props = defineProps({

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