mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 工作流版本管理 (#1386)
This commit is contained in:
parent
0c6e4b73b8
commit
36998c804e
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 4.2.15 on 2024-10-16 15:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
sql = """
|
||||
UPDATE "public".application_work_flow_version
|
||||
SET "name" = TO_CHAR(create_time, 'YYYY-MM-DD HH24:MI:SS');
|
||||
"""
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('application', '0017_application_tts_model_params_setting'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='workflowversion',
|
||||
name='name',
|
||||
field=models.CharField(default='', max_length=128, verbose_name='版本名称'),
|
||||
),
|
||||
migrations.RunSQL(sql),
|
||||
migrations.AddField(
|
||||
model_name='workflowversion',
|
||||
name='publish_user_id',
|
||||
field=models.UUIDField(default=None, null=True, verbose_name='发布者id'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workflowversion',
|
||||
name='publish_user_name',
|
||||
field=models.CharField(default='', max_length=128, verbose_name='发布者名称'),
|
||||
),
|
||||
]
|
||||
|
|
@ -88,6 +88,9 @@ class Application(AppModelMixin):
|
|||
class WorkFlowVersion(AppModelMixin):
|
||||
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id")
|
||||
application = models.ForeignKey(Application, on_delete=models.CASCADE)
|
||||
name = models.CharField(verbose_name="版本名称", max_length=128, default="")
|
||||
publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True)
|
||||
publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="")
|
||||
work_flow = models.JSONField(verbose_name="工作流数据", default=dict)
|
||||
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
@date:2023/11/7 10:02
|
||||
@desc:
|
||||
"""
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
|
|
@ -50,6 +51,7 @@ from setting.models_provider.constants.model_provider_constants import ModelProv
|
|||
from setting.models_provider.tools import get_model_instance_by_model_user_id
|
||||
from setting.serializers.provider_serializers import ModelSerializer
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from users.models import User
|
||||
|
||||
chat_cache = cache.caches['chat_cache']
|
||||
|
||||
|
|
@ -684,6 +686,8 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
def publish(self, instance, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid()
|
||||
user_id = self.data.get('user_id')
|
||||
user = QuerySet(User).filter(id=user_id).first()
|
||||
application = QuerySet(Application).filter(id=self.data.get("application_id")).first()
|
||||
work_flow = instance.get('work_flow')
|
||||
if work_flow is None:
|
||||
|
|
@ -703,7 +707,10 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
application.save()
|
||||
# 插入知识库关联关系
|
||||
self.save_application_mapping(application_dataset_id_list, dataset_id_list, application.id)
|
||||
work_flow_version = WorkFlowVersion(work_flow=work_flow, application=application)
|
||||
work_flow_version = WorkFlowVersion(work_flow=work_flow, application=application,
|
||||
name=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
publish_user_id=user_id,
|
||||
publish_user_name=user.username)
|
||||
chat_cache.clear_by_application_id(str(application.id))
|
||||
work_flow_version.save()
|
||||
return True
|
||||
|
|
@ -1002,6 +1009,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
if application.tts_model_enable:
|
||||
model = get_model_instance_by_model_user_id(application.tts_model_id, application.user_id,
|
||||
**application.tts_model_params_setting)
|
||||
|
||||
return model.text_to_speech(text)
|
||||
|
||||
def play_demo_text(self, form_data, with_valid=True):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: application_version_serializers.py
|
||||
@date:2024/10/15 16:42
|
||||
@desc:
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
from django.db.models import QuerySet
|
||||
from rest_framework import serializers
|
||||
|
||||
from application.models import WorkFlowVersion
|
||||
from common.db.search import page_search
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.util.field_message import ErrMessage
|
||||
|
||||
|
||||
class ApplicationVersionModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = WorkFlowVersion
|
||||
fields = ['id', 'name', 'application_id', 'work_flow', 'publish_user_id', 'publish_user_name', 'create_time',
|
||||
'update_time']
|
||||
|
||||
|
||||
class ApplicationVersionEditSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(required=False, max_length=128, allow_null=True, allow_blank=True,
|
||||
error_messages=ErrMessage.char("版本名称"))
|
||||
|
||||
|
||||
class ApplicationVersionSerializer(serializers.Serializer):
|
||||
class Query(serializers.Serializer):
|
||||
application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.char("应用id"))
|
||||
name = serializers.CharField(required=False, allow_null=True, allow_blank=True,
|
||||
error_messages=ErrMessage.char("摘要"))
|
||||
|
||||
def get_query_set(self):
|
||||
query_set = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'))
|
||||
if 'name' in self.data and self.data.get('name') is not None:
|
||||
query_set = query_set.filter(name__contains=self.data.get('name'))
|
||||
return query_set.order_by("-create_time")
|
||||
|
||||
def list(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
query_set = self.get_query_set()
|
||||
return [ApplicationVersionModelSerializer(v).data for v in query_set]
|
||||
|
||||
def page(self, current_page, page_size, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
return page_search(current_page, page_size,
|
||||
self.get_query_set(),
|
||||
post_records_handler=lambda v: ApplicationVersionModelSerializer(v).data)
|
||||
|
||||
class Operate(serializers.Serializer):
|
||||
application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.char("应用id"))
|
||||
work_flow_version_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("工作流版本id"))
|
||||
|
||||
def one(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
work_flow_version = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'),
|
||||
id=self.data.get('work_flow_version_id')).first()
|
||||
if work_flow_version is not None:
|
||||
return ApplicationVersionModelSerializer(work_flow_version).data
|
||||
else:
|
||||
raise AppApiException(500, '不存在的工作流版本')
|
||||
|
||||
def edit(self, instance: Dict, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
ApplicationVersionEditSerializer(data=instance).is_valid(raise_exception=True)
|
||||
work_flow_version = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'),
|
||||
id=self.data.get('work_flow_version_id')).first()
|
||||
if work_flow_version is not None:
|
||||
name = instance.get('name', None)
|
||||
if name is not None and len(name) > 0:
|
||||
work_flow_version.name = name
|
||||
work_flow_version.save()
|
||||
return ApplicationVersionModelSerializer(work_flow_version).data
|
||||
else:
|
||||
raise AppApiException(500, '不存在的工作流版本')
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: application_version_api.py
|
||||
@date:2024/10/15 17:18
|
||||
@desc:
|
||||
"""
|
||||
from drf_yasg import openapi
|
||||
|
||||
from common.mixins.api_mixin import ApiMixin
|
||||
|
||||
|
||||
class ApplicationVersionApi(ApiMixin):
|
||||
@staticmethod
|
||||
def get_response_body_api():
|
||||
return openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['id', 'name', 'work_flow', 'create_time', 'update_time'],
|
||||
properties={
|
||||
'id': openapi.Schema(type=openapi.TYPE_NUMBER, title="主键id",
|
||||
description="主键id"),
|
||||
'name': openapi.Schema(type=openapi.TYPE_NUMBER, title="版本名称",
|
||||
description="版本名称"),
|
||||
'work_flow': openapi.Schema(type=openapi.TYPE_STRING, title="工作流数据", description='工作流数据'),
|
||||
'create_time': openapi.Schema(type=openapi.TYPE_STRING, title="创建时间", description='创建时间'),
|
||||
'update_time': openapi.Schema(type=openapi.TYPE_STRING, title="修改时间", description='修改时间')
|
||||
}
|
||||
)
|
||||
|
||||
class Query(ApiMixin):
|
||||
@staticmethod
|
||||
def get_request_params_api():
|
||||
return [openapi.Parameter(name='application_id',
|
||||
in_=openapi.IN_PATH,
|
||||
type=openapi.TYPE_STRING,
|
||||
required=True,
|
||||
description='应用id'),
|
||||
openapi.Parameter(name='name',
|
||||
in_=openapi.IN_QUERY,
|
||||
type=openapi.TYPE_STRING,
|
||||
required=False,
|
||||
description='版本名称')]
|
||||
|
||||
class Operate(ApiMixin):
|
||||
@staticmethod
|
||||
def get_request_params_api():
|
||||
return [openapi.Parameter(name='application_id',
|
||||
in_=openapi.IN_PATH,
|
||||
type=openapi.TYPE_STRING,
|
||||
required=True,
|
||||
description='应用id'),
|
||||
openapi.Parameter(name='work_flow_version_id',
|
||||
in_=openapi.IN_PATH,
|
||||
type=openapi.TYPE_STRING,
|
||||
required=True,
|
||||
description='应用版本id'), ]
|
||||
|
||||
class Edit(ApiMixin):
|
||||
@staticmethod
|
||||
def get_request_body_api():
|
||||
return openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=[],
|
||||
properties={
|
||||
'name': openapi.Schema(type=openapi.TYPE_STRING, title="版本名称",
|
||||
description="版本名称")
|
||||
}
|
||||
)
|
||||
|
|
@ -70,7 +70,12 @@ urlpatterns = [
|
|||
name='application/audio'),
|
||||
path('application/<str:application_id>/text_to_speech', views.Application.TextToSpeech.as_view(),
|
||||
name='application/audio'),
|
||||
path('application/<str:application_id>/work_flow_version', views.ApplicationVersionView.as_view()),
|
||||
path('application/<str:application_id>/work_flow_version/<int:current_page>/<int:page_size>',
|
||||
views.ApplicationVersionView.Page.as_view()),
|
||||
path('application/<str:application_id>/work_flow_version/<str:work_flow_version_id>',
|
||||
views.ApplicationVersionView.Operate.as_view()),
|
||||
path('application/<str:application_id>/play_demo_text', views.Application.PlayDemoText.as_view(),
|
||||
name='application/audio'),
|
||||
name='application/audio')
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@
|
|||
"""
|
||||
from .application_views import *
|
||||
from .chat_views import *
|
||||
from .application_version_views import *
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: application_version_views.py
|
||||
@date:2024/10/15 16:49
|
||||
@desc:
|
||||
"""
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from application.serializers.application_version_serializers import ApplicationVersionSerializer
|
||||
from application.swagger_api.application_version_api import ApplicationVersionApi
|
||||
from common.auth import has_permissions, TokenAuth
|
||||
from common.constants.permission_constants import PermissionConstants, CompareConstants, ViewPermission, RoleConstants, \
|
||||
Permission, Group, Operate
|
||||
from common.response import result
|
||||
|
||||
|
||||
class ApplicationVersionView(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
@swagger_auto_schema(operation_summary="获取应用列表",
|
||||
operation_id="获取应用列表",
|
||||
manual_parameters=ApplicationVersionApi.Query.get_request_params_api(),
|
||||
responses=result.get_api_array_response(ApplicationVersionApi.get_response_body_api()),
|
||||
tags=['应用/版本'])
|
||||
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
|
||||
def get(self, request: Request, application_id: str):
|
||||
return result.success(
|
||||
ApplicationVersionSerializer.Query(
|
||||
data={'name': request.query_params.get('name'), 'user_id': request.user.id,
|
||||
'application_id': application_id}).list())
|
||||
|
||||
class Page(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
@swagger_auto_schema(operation_summary="分页获取应用版本列表",
|
||||
operation_id="分页获取应用版本列表",
|
||||
manual_parameters=result.get_page_request_params(
|
||||
ApplicationVersionApi.Query.get_request_params_api()),
|
||||
responses=result.get_page_api_response(ApplicationVersionApi.get_response_body_api()),
|
||||
tags=['应用/版本'])
|
||||
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
|
||||
def get(self, request: Request, application_id: str, current_page: int, page_size: int):
|
||||
return result.success(
|
||||
ApplicationVersionSerializer.Query(
|
||||
data={'name': request.query_params.get('name'), 'user_id': request.user,
|
||||
'application_id': application_id}).page(
|
||||
current_page, page_size))
|
||||
|
||||
class Operate(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
@swagger_auto_schema(operation_summary="获取应用版本详情",
|
||||
operation_id="获取应用版本详情",
|
||||
manual_parameters=ApplicationVersionApi.Operate.get_request_params_api(),
|
||||
responses=result.get_api_response(ApplicationVersionApi.get_response_body_api()),
|
||||
tags=['应用/版本'])
|
||||
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
|
||||
def get(self, request: Request, application_id: str, work_flow_version_id: str):
|
||||
return result.success(
|
||||
ApplicationVersionSerializer.Operate(
|
||||
data={'user_id': request.user,
|
||||
'application_id': application_id, 'work_flow_version_id': work_flow_version_id}).one())
|
||||
|
||||
@action(methods=['PUT'], detail=False)
|
||||
@swagger_auto_schema(operation_summary="修改应用版本信息",
|
||||
operation_id="修改应用版本信息",
|
||||
manual_parameters=ApplicationVersionApi.Operate.get_request_params_api(),
|
||||
request_body=ApplicationVersionApi.Edit.get_request_body_api(),
|
||||
responses=result.get_api_response(ApplicationVersionApi.get_response_body_api()),
|
||||
tags=['应用/版本'])
|
||||
@has_permissions(ViewPermission(
|
||||
[RoleConstants.ADMIN, RoleConstants.USER],
|
||||
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.MANAGE,
|
||||
dynamic_tag=keywords.get('application_id'))],
|
||||
compare=CompareConstants.AND))
|
||||
def put(self, request: Request, application_id: str, work_flow_version_id: str):
|
||||
return result.success(
|
||||
ApplicationVersionSerializer.Operate(
|
||||
data={'application_id': application_id, 'work_flow_version_id': work_flow_version_id,
|
||||
'user_id': request.user.id}).edit(
|
||||
request.data))
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
from typing import Dict, List
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
|
@ -34,3 +35,7 @@ class TencentEmbeddingModel(MaxKBBaseModel, Embeddings):
|
|||
secret_key=model_credential.get('SecretKey'),
|
||||
model_name=model_name,
|
||||
)
|
||||
|
||||
def _generate_auth_token(self):
|
||||
# Example method to generate an authentication token for the model API
|
||||
return f"{self.secret_id}:{self.secret_key}"
|
||||
|
|
|
|||
|
|
@ -415,6 +415,47 @@ const validatePassword: (
|
|||
return get(`/application/${application_id}/auth/${password}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* workflow历史版本
|
||||
*/
|
||||
const getWorkFlowVersion: (
|
||||
application_id: string,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (application_id, loading) => {
|
||||
return get(`/application/${application_id}/work_flow_version`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* workflow历史版本详情
|
||||
*/
|
||||
const getWorkFlowVersionDetail: (
|
||||
application_id: string,
|
||||
application_version_id: string,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (application_id, application_version_id, loading) => {
|
||||
return get(
|
||||
`/application/${application_id}/work_flow_version/${application_version_id}`,
|
||||
undefined,
|
||||
loading
|
||||
)
|
||||
}
|
||||
/**
|
||||
* 修改workflow历史版本
|
||||
*/
|
||||
const putWorkFlowVersion: (
|
||||
application_id: string,
|
||||
application_version_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (application_id, application_version_id, data, loading) => {
|
||||
return put(
|
||||
`/application/${application_id}/work_flow_version/${application_version_id}`,
|
||||
data,
|
||||
undefined,
|
||||
loading
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllAppilcation,
|
||||
getApplication,
|
||||
|
|
@ -448,5 +489,8 @@ export default {
|
|||
updatePlatformConfig,
|
||||
updatePlatformStatus,
|
||||
validatePassword,
|
||||
getWorkFlowVersion,
|
||||
getWorkFlowVersionDetail,
|
||||
putWorkFlowVersion,
|
||||
playDemoText
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
<ul v-if="data.length > 0">
|
||||
<template v-for="(item, index) in data" :key="index">
|
||||
<li
|
||||
@click.prevent="clickHandle(item, index)"
|
||||
@click.stop="clickHandle(item, index)"
|
||||
:class="current === item[props.valueKey] ? 'active' : ''"
|
||||
class="cursor"
|
||||
@mouseenter="mouseenter(item)"
|
||||
@mouseleave="mouseleave()"
|
||||
@mouseenter.stop="mouseenter(item)"
|
||||
@mouseleave.stop="mouseleave()"
|
||||
>
|
||||
<slot :row="item" :index="index"> </slot>
|
||||
</li>
|
||||
|
|
@ -78,6 +78,13 @@ defineExpose({
|
|||
border-radius: 4px;
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
border-radius: 4px;
|
||||
background: var(--app-text-color-light-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1227,5 +1227,26 @@ export const iconMap: any = {
|
|||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
},
|
||||
// 'app-history-outlined': {
|
||||
// iconReader: () => {
|
||||
// return h('i', [
|
||||
// h(
|
||||
// 'svg',
|
||||
// {
|
||||
// style: { height: '100%', width: '100%' },
|
||||
// viewBox: '0 0 1024 1024',
|
||||
// version: '1.1',
|
||||
// xmlns: 'http://www.w3.org/2000/svg'
|
||||
// },
|
||||
// [
|
||||
// h('path', {
|
||||
// d: 'M955.733333 512c0 235.648-191.018667 426.666667-426.666666 426.666667a425.898667 425.898667 0 0 1-334.677334-162.005334l67.797334-51.84a341.333333 341.333333 0 1 0-69.717334-269.653333h30.08c18.176-0.042667 29.013333 20.181333 18.944 35.328L170.24 597.333333a22.741333 22.741333 0 0 1-37.888 0l-71.253333-106.88a22.741333 22.741333 0 0 1 18.944-35.413333h26.112C133.973333 246.4 312.746667 85.333333 529.066667 85.333333c235.648 0 426.666667 191.018667 426.666666 426.666667z" p-id="16742"></path><path d="M554.666667 497.792V364.074667A22.741333 22.741333 0 0 0 531.925333 341.333333h-39.850666a22.741333 22.741333 0 0 0-22.741334 22.741334v196.266666c0 12.586667 10.197333 22.784 22.741334 22.784H674.133333a22.741333 22.741333 0 0 0 22.741334-22.784V520.533333a22.741333 22.741333 0 0 0-22.741334-22.741333H554.666667z',
|
||||
// fill: 'currentColor'
|
||||
// })
|
||||
// ]
|
||||
// )
|
||||
// ])
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,25 +65,44 @@ const props = defineProps({
|
|||
trigger: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validator: (value: string) => ['default', 'dblclick'].includes(value)
|
||||
validator: (value: string) => ['default', 'dblclick', 'manual'].includes(value)
|
||||
},
|
||||
write: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['change'])
|
||||
const emit = defineEmits(['change', 'close'])
|
||||
const inputRef = ref()
|
||||
const isEdit = ref(false)
|
||||
const writeValue = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
watch(isEdit, (bool) => {
|
||||
console.log(bool)
|
||||
if (!bool) {
|
||||
writeValue.value = ''
|
||||
emit('close')
|
||||
} else {
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
})
|
||||
setTimeout(() => {
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.write,
|
||||
(bool) => {
|
||||
if (bool && props.trigger === 'manual') {
|
||||
editNameHandle()
|
||||
} else {
|
||||
isEdit.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function dblclick() {
|
||||
if (props.trigger === 'dblclick') {
|
||||
editNameHandle()
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ h5 {
|
|||
|
||||
/* tag */
|
||||
.default-tag {
|
||||
background: var(--el-color-primary-light-7);
|
||||
background: var(--el-color-primary-light-8);
|
||||
color: var(--el-color-primary);
|
||||
border: none;
|
||||
}
|
||||
|
|
@ -492,6 +492,9 @@ h5 {
|
|||
.avatar-green {
|
||||
background: #34c724;
|
||||
}
|
||||
.avatar-grey {
|
||||
background: #bbbfc4;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--el-color-success);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
<div class="workflow-publish-history border-l">
|
||||
<h4 class="border-b p-16-24">发布历史</h4>
|
||||
<div class="left-height pt-0">
|
||||
<el-scrollbar>
|
||||
<div class="p-8 pt-0">
|
||||
<common-list
|
||||
:data="LogData"
|
||||
class="mt-8"
|
||||
v-loading="loading"
|
||||
@click.stop="clickListHandle"
|
||||
@mouseenter="mouseenter"
|
||||
@mouseleave="mouseId = ''"
|
||||
>
|
||||
<template #default="{ row, index }">
|
||||
<div class="flex-between">
|
||||
<div style="max-width: 80%">
|
||||
<h5 :class="index === 0 ? 'primary' : ''" class="flex">
|
||||
<ReadWrite
|
||||
@change="editName($event, row)"
|
||||
:data="row.name || datetimeFormat(row.update_time)"
|
||||
trigger="manual"
|
||||
:write="row.writeStatus"
|
||||
@close="closeWrite(row)"
|
||||
/>
|
||||
<el-tag v-if="index === 0" class="default-tag ml-4">最近发布</el-tag>
|
||||
</h5>
|
||||
<el-text type="info" class="color-secondary flex mt-8">
|
||||
<AppAvatar :size="20" class="avatar-grey mr-4">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
</AppAvatar>
|
||||
{{ row.publish_user_name }}
|
||||
</el-text>
|
||||
</div>
|
||||
|
||||
<div @click.stop v-show="mouseId === row.id">
|
||||
<el-dropdown trigger="click" :teleported="false">
|
||||
<el-button text>
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click.stop="openEditVersion(row)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
编辑
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="refreshVersion(row)">
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
恢复此版本
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<div class="text-center">
|
||||
<el-text type="info">暂无历史记录</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import applicationApi from '@/api/application'
|
||||
import { datetimeFormat } from '@/utils/time'
|
||||
import { MsgSuccess, MsgError } from '@/utils/message'
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['click', 'refreshVersion'])
|
||||
const loading = ref(false)
|
||||
const LogData = ref<any[]>([])
|
||||
|
||||
const mouseId = ref('')
|
||||
|
||||
function mouseenter(row: any) {
|
||||
mouseId.value = row.id
|
||||
}
|
||||
|
||||
function clickListHandle(item: any) {
|
||||
emit('click', item)
|
||||
}
|
||||
|
||||
function refreshVersion(item: any) {
|
||||
emit('refreshVersion', item)
|
||||
}
|
||||
|
||||
function openEditVersion(item: any) {
|
||||
item['writeStatus'] = true
|
||||
}
|
||||
|
||||
function closeWrite(item: any) {
|
||||
item['writeStatus'] = false
|
||||
}
|
||||
|
||||
function editName(val: string, item: any) {
|
||||
if (val) {
|
||||
const obj = {
|
||||
name: val
|
||||
}
|
||||
applicationApi.putWorkFlowVersion(id as string, item.id, obj, loading).then(() => {
|
||||
MsgSuccess('修改成功')
|
||||
item['writeStatus'] = false
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
MsgError('名字不能为空!')
|
||||
}
|
||||
}
|
||||
|
||||
function getList() {
|
||||
applicationApi.getWorkFlowVersion(id, loading).then((res: any) => {
|
||||
LogData.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.workflow-publish-history {
|
||||
width: 320px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 57px;
|
||||
background: #ffffff;
|
||||
height: calc(100vh - 57px);
|
||||
z-index: 9;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,11 +6,24 @@
|
|||
@click="router.push({ path: `/application/${id}/WORK_FLOW/overview` })"
|
||||
></back-button>
|
||||
<h4>{{ detail?.name }}</h4>
|
||||
<el-text type="info" class="ml-16 color-secondary" v-if="saveTime"
|
||||
<div v-if="showHistory && disablePublic">
|
||||
<el-text type="info" class="ml-16 color-secondary"
|
||||
>预览版本:
|
||||
{{ currentVersion.name || datetimeFormat(currentVersion.update_time) }}</el-text
|
||||
>
|
||||
</div>
|
||||
<el-text type="info" class="ml-16 color-secondary" v-else-if="saveTime"
|
||||
>保存时间:{{ datetimeFormat(saveTime) }}</el-text
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="showHistory && disablePublic">
|
||||
<el-button type="primary" class="mr-8" @click="refreshVersion()"> 恢复版本 </el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button text @click="closeHistory">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-button icon="Plus" @click="showPopover = !showPopover"> 添加组件 </el-button>
|
||||
<el-button @click="clickShowDebug" :disabled="showDebug">
|
||||
<AppIcon iconName="app-play-outlined" class="mr-4"></AppIcon>
|
||||
|
|
@ -21,6 +34,27 @@
|
|||
保存
|
||||
</el-button>
|
||||
<el-button type="primary" @click="publicHandle"> 发布 </el-button>
|
||||
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text @click.stop class="ml-8 mt-4">
|
||||
<el-icon class="rotate-90"><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openHistory">
|
||||
<AppIcon iconName="app-history-outlined"></AppIcon>
|
||||
发布历史
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<AppIcon iconName="app-save-outlined"></AppIcon>
|
||||
自动保存
|
||||
<div @click.stop class="ml-4">
|
||||
<el-switch size="small" v-model="isSave" @change="changeSave" />
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下拉框 -->
|
||||
|
|
@ -90,6 +124,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
<!-- 发布历史 -->
|
||||
<PublishHistory
|
||||
v-if="showHistory"
|
||||
@click="checkVersion"
|
||||
v-click-outside="clickoutsideHistory"
|
||||
@refreshVersion="refreshVersion"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -97,6 +138,7 @@ import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
|
|||
import { useRouter, useRoute } from 'vue-router'
|
||||
import Workflow from '@/workflow/index.vue'
|
||||
import DropdownMenu from '@/views/application-workflow/component/DropdownMenu.vue'
|
||||
import PublishHistory from '@/views/application-workflow/component/PublishHistory.vue'
|
||||
import applicationApi from '@/api/application'
|
||||
import { isAppIcon } from '@/utils/application'
|
||||
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
||||
|
|
@ -126,6 +168,57 @@ const showPopover = ref(false)
|
|||
const showDebug = ref(false)
|
||||
const enlarge = ref(false)
|
||||
const saveTime = ref<any>('')
|
||||
const isSave = ref(false)
|
||||
const showHistory = ref(false)
|
||||
const disablePublic = ref(false)
|
||||
const currentVersion = ref<any>({})
|
||||
|
||||
function clickoutsideHistory() {
|
||||
if (!disablePublic.value) {
|
||||
showHistory.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function refreshVersion(item?: any) {
|
||||
if (item) {
|
||||
getHistortyDetail(item.id)
|
||||
}
|
||||
initInterval()
|
||||
showHistory.value = false
|
||||
disablePublic.value = false
|
||||
}
|
||||
function checkVersion(item: any) {
|
||||
disablePublic.value = true
|
||||
getHistortyDetail(item.id)
|
||||
currentVersion.value = item
|
||||
closeInterval()
|
||||
}
|
||||
|
||||
function getHistortyDetail(versionId: string) {
|
||||
applicationApi.getWorkFlowVersionDetail(id, versionId, loading).then((res: any) => {
|
||||
res.data?.work_flow['nodes'].map((v: any) => {
|
||||
v['properties']['noRender'] = true
|
||||
})
|
||||
detail.value.stt_model_id = res.data.stt_model
|
||||
detail.value.tts_model_id = res.data.tts_model
|
||||
detail.value.tts_type = res.data.tts_type
|
||||
saveTime.value = res.data?.update_time
|
||||
})
|
||||
}
|
||||
|
||||
function closeHistory() {
|
||||
getDetail()
|
||||
showHistory.value = false
|
||||
}
|
||||
|
||||
function openHistory() {
|
||||
showHistory.value = true
|
||||
}
|
||||
|
||||
function changeSave(bool: boolean) {
|
||||
bool ? initInterval() : closeInterval()
|
||||
localStorage.setItem('workflowAutoSave', bool.toString())
|
||||
}
|
||||
|
||||
function clickNodes(item: any) {
|
||||
// workflowRef.value?.addNode(item)
|
||||
|
|
@ -136,6 +229,7 @@ function onmousedown(item: any) {
|
|||
// workflowRef.value?.onmousedown(item)
|
||||
showPopover.value = false
|
||||
}
|
||||
|
||||
function clickoutside() {
|
||||
showPopover.value = false
|
||||
}
|
||||
|
|
@ -256,9 +350,10 @@ const closeInterval = () => {
|
|||
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
|
||||
const workflowAutoSave = localStorage.getItem('workflowAutoSave')
|
||||
isSave.value = workflowAutoSave === 'true' ? true : false
|
||||
// 初始化定时任务
|
||||
if (hasPermission(`APPLICATION:MANAGE:${id}`, 'AND')) {
|
||||
if (hasPermission(`APPLICATION:MANAGE:${id}`, 'AND') && workflowAutoSave) {
|
||||
initInterval()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<el-scrollbar max-height="300">
|
||||
<div class="p-8">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
:data="chatLogData"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
@click="clickListHandle"
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<div v-if="chatLogData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
|
@ -115,7 +115,6 @@ const isDefaultTheme = computed(() => {
|
|||
const AiChatRef = ref()
|
||||
const loading = ref(false)
|
||||
const left_loading = ref(false)
|
||||
|
||||
const chatLogeData = ref<any[]>([])
|
||||
const show = ref(false)
|
||||
const props = defineProps<{
|
||||
|
|
@ -185,7 +184,7 @@ function getChatLog(id: string) {
|
|||
}
|
||||
|
||||
log.asyncGetChatLogClient(id, page, left_loading).then((res: any) => {
|
||||
chatLogeData.value = res.data.records
|
||||
chatLogData.value = res.data.records
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
<el-scrollbar>
|
||||
<div class="p-8 pt-0">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
:data="chatLogData"
|
||||
class="mt-8"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<div v-if="chatLogData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
|
@ -161,6 +161,7 @@ const AiChatRef = ref()
|
|||
const loading = ref(false)
|
||||
const left_loading = ref(false)
|
||||
|
||||
|
||||
const applicationDetail = computed({
|
||||
get: () => {
|
||||
return props.application_profile
|
||||
|
|
@ -212,11 +213,11 @@ function handleScroll(event: any) {
|
|||
}
|
||||
|
||||
function newChat() {
|
||||
if (!chatLogeData.value.some((v) => v.id === 'new')) {
|
||||
if (!chatLogData.value.some((v) => v.id === 'new')) {
|
||||
paginationConfig.value.current_page = 1
|
||||
paginationConfig.value.total = 0
|
||||
currentRecordList.value = []
|
||||
chatLogeData.value.unshift(newObj)
|
||||
chatLogData.value.unshift(newObj)
|
||||
} else {
|
||||
paginationConfig.value.current_page = 1
|
||||
paginationConfig.value.total = 0
|
||||
|
|
@ -236,9 +237,9 @@ function getChatLog(id: string, refresh?: boolean) {
|
|||
}
|
||||
|
||||
log.asyncGetChatLogClient(id, page, left_loading).then((res: any) => {
|
||||
chatLogeData.value = res.data.records
|
||||
chatLogData.value = res.data.records
|
||||
if (refresh) {
|
||||
currentChatName.value = chatLogeData.value[0].abstract
|
||||
currentChatName.value = chatLogData.value[0].abstract
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue