Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/iot
# Conflicts: # pnpm-lock.yaml
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click.stop>
|
||||
<Dialog
|
||||
v-model="dialogVisible"
|
||||
:canFullscreen="false"
|
||||
@@ -181,6 +181,7 @@ function openModal() {
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
debugger
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
122
src/components/DeptSelectForm/index.vue
Normal file
122
src/components/DeptSelectForm/index.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="部门选择" width="600">
|
||||
<el-row v-loading="formLoading">
|
||||
<el-col :span="24">
|
||||
<ContentWrap class="h-1/1">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="deptTree"
|
||||
:props="defaultProps"
|
||||
show-checkbox
|
||||
:check-strictly="checkStrictly"
|
||||
check-on-click-node
|
||||
default-expand-all
|
||||
highlight-current
|
||||
node-key="id"
|
||||
@check="handleCheck"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<el-button
|
||||
:disabled="formLoading || !selectedDeptIds?.length"
|
||||
type="primary"
|
||||
@click="submitForm"
|
||||
>
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
|
||||
defineOptions({ name: 'DeptSelectForm' })
|
||||
|
||||
const emit = defineEmits<{
|
||||
confirm: [deptList: any[]]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps({
|
||||
// 是否严格的遵循父子不互相关联
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否支持多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const treeRef = ref()
|
||||
const deptTree = ref<Tree[]>([]) // 部门树形结构
|
||||
const selectedDeptIds = ref<number[]>([]) // 选中的部门 ID 列表
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (selectedList?: DeptApi.DeptVO[]) => {
|
||||
resetForm()
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 加载部门列表
|
||||
const deptData = await DeptApi.getSimpleDeptList()
|
||||
deptTree.value = handleTree(deptData)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
dialogVisible.value = true
|
||||
// 设置已选择的部门
|
||||
if (selectedList?.length) {
|
||||
await nextTick()
|
||||
const selectedIds = selectedList
|
||||
.map((dept) => dept.id)
|
||||
.filter((id): id is number => id !== undefined)
|
||||
selectedDeptIds.value = selectedIds
|
||||
treeRef.value?.setCheckedKeys(selectedIds)
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理选中状态变化 */
|
||||
const handleCheck = (data: any, checked: any) => {
|
||||
selectedDeptIds.value = treeRef.value.getCheckedKeys()
|
||||
if (!props.multiple && selectedDeptIds.value.length > 1) {
|
||||
// 单选模式下,只保留最后选择的节点
|
||||
const lastSelectedId = selectedDeptIds.value[selectedDeptIds.value.length - 1]
|
||||
selectedDeptIds.value = [lastSelectedId]
|
||||
treeRef.value.setCheckedKeys([lastSelectedId])
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交选择 */
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
// 获取选中的完整部门数据
|
||||
const checkedNodes = treeRef.value.getCheckedNodes()
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
emit('confirm', checkedNodes)
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
deptTree.value = []
|
||||
selectedDeptIds.value = []
|
||||
if (treeRef.value) {
|
||||
treeRef.value.setCheckedKeys([])
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
||||
@@ -68,6 +68,7 @@ const dialogStyle = computed(() => {
|
||||
draggable
|
||||
class="com-dialog"
|
||||
:show-close="false"
|
||||
@close="$emit('update:modelValue', false)"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<div class="relative h-54px flex items-center justify-between pl-15px pr-15px">
|
||||
@@ -90,7 +91,7 @@ const dialogStyle = computed(() => {
|
||||
icon="ep:close"
|
||||
hover-color="var(--el-color-primary)"
|
||||
color="var(--el-color-info)"
|
||||
@click="close"
|
||||
@click.stop="close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ export const CouponDiscount = defineComponent({
|
||||
setup(props) {
|
||||
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
||||
// 折扣
|
||||
let value = coupon.discountPercent + ''
|
||||
let value = coupon.discountPercent / 10 + ''
|
||||
let suffix = ' 折'
|
||||
// 满减
|
||||
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||
@@ -43,7 +43,7 @@ export const CouponDiscountDesc = defineComponent({
|
||||
const discountDesc =
|
||||
coupon.discountType === PromotionDiscountTypeEnum.PRICE.type
|
||||
? `减${floatToFixed2(coupon.discountPrice)}元`
|
||||
: `打${coupon.discountPercent}折`
|
||||
: `打${coupon.discountPercent / 10.0}折`
|
||||
return () => (
|
||||
<div>
|
||||
<span>{useCondition}</span>
|
||||
|
||||
@@ -49,7 +49,13 @@
|
||||
<div class="flex flex-col justify-evenly gap-4px">
|
||||
<!-- 优惠值 -->
|
||||
<CouponDiscount :coupon="coupon" />
|
||||
<div>{{ coupon.name }}</div>
|
||||
<!-- 优惠描述 -->
|
||||
<CouponDiscountDesc :coupon="coupon" />
|
||||
<!-- 领取说明 -->
|
||||
<div v-if="coupon.totalCount >= 0">
|
||||
仅剩:{{ coupon.totalCount - coupon.takeCount }}张
|
||||
</div>
|
||||
<div v-else-if="coupon.totalCount === -1">仅剩:不限制</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
@@ -67,7 +73,8 @@
|
||||
<div v-else class="flex flex-col items-center justify-around gap-4px p-4px">
|
||||
<!-- 优惠值 -->
|
||||
<CouponDiscount :coupon="coupon" />
|
||||
<div>{{ coupon.name }}</div>
|
||||
<!-- 优惠描述 -->
|
||||
<CouponDiscountDesc :coupon="coupon" />
|
||||
<div
|
||||
class="rounded-20px p-x-8px p-y-2px"
|
||||
:style="{
|
||||
@@ -124,7 +131,7 @@ watch(
|
||||
() => {
|
||||
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
||||
couponWidth.value =
|
||||
(phoneWidth.value * 0.95 - props.property.space * (props.property.columns - 1)) /
|
||||
(phoneWidth.value - props.property.space * (props.property.columns - 1)) /
|
||||
props.property.columns
|
||||
// 显示滚动条
|
||||
scrollbarWidth.value = `${
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative"
|
||||
:style="{ height: `${rowCount * CUBE_SIZE}px`, width: `${4 * CUBE_SIZE}px` }"
|
||||
:style="{
|
||||
height: `${rowCount * CUBE_SIZE}px`,
|
||||
width: `${4 * CUBE_SIZE}px`,
|
||||
padding: `${property.space}px`
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in property.list"
|
||||
:key="index"
|
||||
class="absolute"
|
||||
:style="{
|
||||
width: `${item.width * CUBE_SIZE - property.space * 2}px`,
|
||||
height: `${item.height * CUBE_SIZE - property.space * 2}px`,
|
||||
margin: `${property.space}px`,
|
||||
width: `${item.width * CUBE_SIZE - property.space}px`,
|
||||
height: `${item.height * CUBE_SIZE - property.space}px`,
|
||||
top: `${item.top * CUBE_SIZE}px`,
|
||||
left: `${item.left * CUBE_SIZE}px`
|
||||
}"
|
||||
@@ -63,10 +66,10 @@ const rowCount = computed(() => {
|
||||
let count = 0
|
||||
if (props.property.list.length > 0) {
|
||||
// 最大行号
|
||||
count = Math.max(...props.property.list.map((item) => item.bottom))
|
||||
count = Math.max(...props.property.list.map((item) => item.top + item.height))
|
||||
}
|
||||
// 行号从 0 开始,所以加 1
|
||||
return count + 1
|
||||
// 保证至少有一行
|
||||
return count == 0 ? 1 : count
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</template>
|
||||
|
||||
@@ -51,7 +51,7 @@ const props = defineProps<{ property: MenuSwiperProperty }>()
|
||||
// 标题的高度
|
||||
const TITLE_HEIGHT = 20
|
||||
// 图标的高度
|
||||
const ICON_SIZE = 42
|
||||
const ICON_SIZE = 32
|
||||
// 垂直间距:一行上下的间距
|
||||
const SPACE_Y = 16
|
||||
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
<ColorInput v-model="formData.bgColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="背景图片" prop="bgImg" v-else>
|
||||
<UploadImg v-model="formData.bgImg" :limit="1" width="56px" height="56px" />
|
||||
<div class="flex items-center">
|
||||
<UploadImg v-model="formData.bgImg" :limit="1" width="56px" height="56px" />
|
||||
<span class="text-xs text-gray-400 ml-2 mb-2">建议宽度:750</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-card class="property-group" shadow="never">
|
||||
<template #header>
|
||||
@@ -39,8 +42,9 @@
|
||||
<el-checkbox
|
||||
v-model="formData._local.previewMp"
|
||||
@change="formData._local.previewOther = !formData._local.previewMp"
|
||||
>预览</el-checkbox
|
||||
>
|
||||
预览
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
@@ -54,8 +58,9 @@
|
||||
<el-checkbox
|
||||
v-model="formData._local.previewOther"
|
||||
@change="formData._local.previewMp = !formData._local.previewOther"
|
||||
>预览</el-checkbox
|
||||
>
|
||||
预览
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -82,8 +82,8 @@ export const component = {
|
||||
bgEndColor: '#FE832A',
|
||||
imgUrl: ''
|
||||
},
|
||||
borderRadiusTop: 8,
|
||||
borderRadiusBottom: 8,
|
||||
borderRadiusTop: 6,
|
||||
borderRadiusBottom: 6,
|
||||
space: 8,
|
||||
spuIds: [],
|
||||
style: {
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
:key="index"
|
||||
>
|
||||
<!-- 角标 -->
|
||||
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||
<div
|
||||
v-if="property.badge.show && property.badge.imgUrl"
|
||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||
>
|
||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||
</div>
|
||||
<!-- 商品封面图 -->
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 表单 -->
|
||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<el-card header="搜索热词" class="property-group" shadow="never">
|
||||
<Draggable v-model="formData.hotKeywords" :empty-item="''">
|
||||
<Draggable v-model="formData.hotKeywords" :empty-item="''" :min="0">
|
||||
<template #default="{ index }">
|
||||
<el-input v-model="formData.hotKeywords[index]" placeholder="请输入热词" />
|
||||
</template>
|
||||
@@ -61,6 +61,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
|
||||
import { isString } from '@/utils/is'
|
||||
|
||||
/** 搜索框属性面板 */
|
||||
defineOptions({ name: 'SearchProperty' })
|
||||
@@ -68,6 +69,19 @@ defineOptions({ name: 'SearchProperty' })
|
||||
const props = defineProps<{ modelValue: SearchProperty }>()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const formData = useVModel(props, 'modelValue', emit)
|
||||
|
||||
// 监听热词数组变化
|
||||
watch(
|
||||
() => formData.value.hotKeywords,
|
||||
(newVal) => {
|
||||
// 找到非字符串项的索引
|
||||
const nonStringIndex = newVal.findIndex((item) => !isString(item))
|
||||
if (nonStringIndex !== -1) {
|
||||
formData.value.hotKeywords[nonStringIndex] = ''
|
||||
}
|
||||
},
|
||||
{ deep: true, flush: 'post' }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util'
|
||||
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 标题栏属性 */
|
||||
export interface TitleBarProperty {
|
||||
// 背景图
|
||||
bgImgUrl: string
|
||||
// 偏移
|
||||
marginLeft: number
|
||||
// 显示位置
|
||||
@@ -22,6 +24,8 @@ export interface TitleBarProperty {
|
||||
titleColor: string
|
||||
// 描述颜色
|
||||
descriptionColor: string
|
||||
// 高度
|
||||
height: number
|
||||
// 查看更多
|
||||
more: {
|
||||
// 是否显示查看更多
|
||||
@@ -52,6 +56,8 @@ export const component = {
|
||||
descriptionWeight: 200,
|
||||
titleColor: 'rgba(50, 50, 51, 10)',
|
||||
descriptionColor: 'rgba(150, 151, 153, 10)',
|
||||
marginLeft: 0,
|
||||
height: 40,
|
||||
more: {
|
||||
//查看更多
|
||||
show: false,
|
||||
|
||||
@@ -1,55 +1,49 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
background:
|
||||
property.style.bgType === 'color' ? property.style.bgColor : `url(${property.style.bgImg})`,
|
||||
backgroundSize: '100% 100%',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}"
|
||||
class="title-bar"
|
||||
>
|
||||
<!-- 内容 -->
|
||||
<div>
|
||||
<div class="title-bar" :style="{ height: `${property.height}px` }">
|
||||
<el-image v-if="property.bgImgUrl" :src="property.bgImgUrl" fit="cover" class="w-full" />
|
||||
<div class="absolute left-0 top-0 w-full h-full flex flex-col justify-center">
|
||||
<!-- 标题 -->
|
||||
<div
|
||||
v-if="property.title"
|
||||
:style="{
|
||||
fontSize: `${property.titleSize}px`,
|
||||
fontWeight: property.titleWeight,
|
||||
color: property.titleColor,
|
||||
textAlign: property.textAlign
|
||||
textAlign: property.textAlign,
|
||||
marginLeft: `${property.marginLeft}px`,
|
||||
marginBottom: '4px'
|
||||
}"
|
||||
v-if="property.title"
|
||||
>
|
||||
{{ property.title }}
|
||||
</div>
|
||||
<!-- 副标题 -->
|
||||
<div
|
||||
v-if="property.description"
|
||||
:style="{
|
||||
fontSize: `${property.descriptionSize}px`,
|
||||
fontWeight: property.descriptionWeight,
|
||||
color: property.descriptionColor,
|
||||
textAlign: property.textAlign
|
||||
textAlign: property.textAlign,
|
||||
marginLeft: `${property.marginLeft}px`
|
||||
}"
|
||||
class="m-t-8px"
|
||||
v-if="property.description"
|
||||
>
|
||||
{{ property.description }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 更多 -->
|
||||
<div
|
||||
class="more"
|
||||
v-show="property.more.show"
|
||||
:style="{
|
||||
color: property.descriptionColor
|
||||
}"
|
||||
class="more"
|
||||
>
|
||||
<span v-if="property.more.type !== 'icon'"> {{ property.more.text }} </span>
|
||||
<Icon v-if="property.more.type !== 'text'" icon="ep:arrow-right" />
|
||||
<Icon icon="ep:arrow-right" v-if="property.more.type !== 'text'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
<script setup lang="ts">
|
||||
import { TitleBarProperty } from './config'
|
||||
|
||||
/** 标题栏 */
|
||||
@@ -57,7 +51,7 @@ defineOptions({ name: 'TitleBar' })
|
||||
|
||||
defineProps<{ property: TitleBarProperty }>()
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.title-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<ComponentContainerProperty v-model="formData.style">
|
||||
<el-form :model="formData" :rules="rules" label-width="85px">
|
||||
<el-card class="property-group" header="风格" shadow="never">
|
||||
<el-form label-width="85px" :model="formData" :rules="rules">
|
||||
<el-card header="风格" class="property-group" shadow="never">
|
||||
<el-form-item label="背景图片" prop="bgImgUrl">
|
||||
<UploadImg v-model="formData.bgImgUrl" width="100%" height="40px">
|
||||
<template #tip>建议尺寸 750*80</template>
|
||||
</UploadImg>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题位置" prop="textAlign">
|
||||
<el-radio-group v-model="formData!.textAlign">
|
||||
<el-tooltip content="居左" placement="top">
|
||||
@@ -16,66 +21,84 @@
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="偏移量" prop="marginLeft" label-width="70px">
|
||||
<el-slider
|
||||
v-model="formData.marginLeft"
|
||||
:max="100"
|
||||
:min="0"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="高度" prop="height" label-width="70px">
|
||||
<el-slider
|
||||
v-model="formData.height"
|
||||
:max="200"
|
||||
:min="20"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="property-group" header="主标题" shadow="never">
|
||||
<el-form-item label="文字" label-width="40px" prop="title">
|
||||
<el-card header="主标题" class="property-group" shadow="never">
|
||||
<el-form-item label="文字" prop="title" label-width="40px">
|
||||
<InputWithColor
|
||||
v-model="formData.title"
|
||||
v-model:color="formData.titleColor"
|
||||
maxlength="20"
|
||||
show-word-limit
|
||||
maxlength="20"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="大小" label-width="40px" prop="titleSize">
|
||||
<el-form-item label="大小" prop="titleSize" label-width="40px">
|
||||
<el-slider
|
||||
v-model="formData.titleSize"
|
||||
:max="60"
|
||||
:min="10"
|
||||
input-size="small"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="粗细" label-width="40px" prop="titleWeight">
|
||||
<el-form-item label="粗细" prop="titleWeight" label-width="40px">
|
||||
<el-slider
|
||||
v-model="formData.titleWeight"
|
||||
:max="900"
|
||||
:min="100"
|
||||
:max="900"
|
||||
:step="100"
|
||||
input-size="small"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="property-group" header="副标题" shadow="never">
|
||||
<el-form-item label="文字" label-width="40px" prop="description">
|
||||
<el-card header="副标题" class="property-group" shadow="never">
|
||||
<el-form-item label="文字" prop="description" label-width="40px">
|
||||
<InputWithColor
|
||||
v-model="formData.description"
|
||||
v-model:color="formData.descriptionColor"
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="大小" label-width="40px" prop="descriptionSize">
|
||||
<el-form-item label="大小" prop="descriptionSize" label-width="40px">
|
||||
<el-slider
|
||||
v-model="formData.descriptionSize"
|
||||
:max="60"
|
||||
:min="10"
|
||||
input-size="small"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="粗细" label-width="40px" prop="descriptionWeight">
|
||||
<el-form-item label="粗细" prop="descriptionWeight" label-width="40px">
|
||||
<el-slider
|
||||
v-model="formData.descriptionWeight"
|
||||
:max="900"
|
||||
:min="100"
|
||||
:max="900"
|
||||
:step="100"
|
||||
input-size="small"
|
||||
show-input
|
||||
input-size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card class="property-group" header="查看更多" shadow="never">
|
||||
<el-card header="查看更多" class="property-group" shadow="never">
|
||||
<el-form-item label="是否显示" prop="more.show">
|
||||
<el-checkbox v-model="formData.more.show" />
|
||||
</el-form-item>
|
||||
@@ -88,7 +111,7 @@
|
||||
<el-radio value="all">文字+图标</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="formData.more.type !== 'icon'" label="更多文字" prop="more.text">
|
||||
<el-form-item label="更多文字" prop="more.text" v-show="formData.more.type !== 'icon'">
|
||||
<el-input v-model="formData.more.text" />
|
||||
</el-form-item>
|
||||
<el-form-item label="跳转链接" prop="more.url">
|
||||
@@ -99,7 +122,7 @@
|
||||
</el-form>
|
||||
</ComponentContainerProperty>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
<script setup lang="ts">
|
||||
import { TitleBarProperty } from './config'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
// 导航栏属性面板
|
||||
@@ -113,4 +136,4 @@ const formData = useVModel(props, 'modelValue', emit)
|
||||
const rules = {}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<Icon
|
||||
icon="ep:delete"
|
||||
class="cursor-pointer text-red-5"
|
||||
v-if="formData.length > 1"
|
||||
v-if="formData.length > min"
|
||||
@click="handleDelete(index)"
|
||||
/>
|
||||
</el-tooltip>
|
||||
@@ -69,7 +69,9 @@ const props = defineProps({
|
||||
// 空的元素:点击添加按钮时,创建元素并添加到列表;默认为空对象
|
||||
emptyItem: any<unknown>().def({}),
|
||||
// 数量限制:默认为0,表示不限制
|
||||
limit: propTypes.number.def(0)
|
||||
limit: propTypes.number.def(0),
|
||||
// 最小数量:默认为1
|
||||
min: propTypes.number.def(1)
|
||||
})
|
||||
// 定义事件
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
@@ -69,11 +69,18 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
||||
if (isEmpty(props.url)) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (props.method) {
|
||||
case 'GET':
|
||||
let url: string = props.url
|
||||
if (props.remote) {
|
||||
url = `${url}?${props.remoteField}=${queryParam.value}`
|
||||
if (queryParam.value != undefined) {
|
||||
if (url.includes('?')) {
|
||||
url = `${url}&${props.remoteField}=${queryParam.value}`
|
||||
} else {
|
||||
url = `${url}?${props.remoteField}=${queryParam.value}`
|
||||
}
|
||||
}
|
||||
}
|
||||
parseOptions(await request.get({ url: url }))
|
||||
break
|
||||
|
||||
@@ -17,6 +17,7 @@ export const useSelectRule = (option: SelectRuleOption) => {
|
||||
icon: option.icon,
|
||||
label,
|
||||
name,
|
||||
event: option.event,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
|
||||
@@ -46,5 +46,6 @@ export interface SelectRuleOption {
|
||||
label: string // label 名称
|
||||
name: string // 组件名称
|
||||
icon: string // 组件图标
|
||||
props?: any[] // 组件规则
|
||||
props?: any[], // 组件规则
|
||||
event?: any[] // 事件配置
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ export const useFormCreateDesigner = async (designer: Ref) => {
|
||||
name: 'ApiSelect',
|
||||
label: '接口选择器',
|
||||
icon: 'icon-server',
|
||||
props: [...apiSelectRule]
|
||||
props: [...apiSelectRule],
|
||||
event: ['click', 'change', 'visibleChange', 'clear', 'blur', 'focus']
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,13 +35,13 @@
|
||||
>
|
||||
<!-- 右上角热区删除按钮 -->
|
||||
<div
|
||||
v-if="selectedHotAreaIndex === index"
|
||||
v-if="selectedHotAreaIndex === index && hotArea.width && hotArea.height"
|
||||
class="btn-delete"
|
||||
@click="handleDeleteHotArea(index)"
|
||||
>
|
||||
<Icon icon="ep:circle-close-filled" />
|
||||
</div>
|
||||
{{ `${hotArea.width}×${hotArea.height}` }}
|
||||
<span v-if="hotArea.width">{{ `${hotArea.width}×${hotArea.height}` }}</span>
|
||||
</div>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -91,6 +91,7 @@ import {
|
||||
DEFAULT_CONDITION_GROUP_VALUE
|
||||
} from './consts'
|
||||
import { generateUUID } from '@/utils'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
defineOptions({
|
||||
name: 'NodeHandler'
|
||||
@@ -184,7 +185,7 @@ const addNode = (type: number) => {
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -242,7 +243,7 @@ const addNode = (type: number) => {
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -59,6 +59,11 @@ const props = defineProps({
|
||||
startUserIds: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
// 可发起流程的部门编号
|
||||
startDeptIds: {
|
||||
type: Array,
|
||||
required: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -82,6 +87,7 @@ provide('deptList', deptOptions)
|
||||
provide('userGroupList', userGroupOptions)
|
||||
provide('deptTree', deptTreeOptions)
|
||||
provide('startUserIds', props.startUserIds)
|
||||
provide('startDeptIds', props.startDeptIds)
|
||||
provide('tasks', [])
|
||||
provide('processInstance', {})
|
||||
const message = useMessage() // 国际化
|
||||
|
||||
@@ -25,21 +25,46 @@
|
||||
</template>
|
||||
<el-tabs type="border-card" v-model="activeTabName">
|
||||
<el-tab-pane label="权限" name="user">
|
||||
<el-text v-if="!startUserIds || startUserIds.length === 0"> 全部成员可以发起流程 </el-text>
|
||||
<el-text v-else-if="startUserIds.length == 1">
|
||||
{{ getUserNicknames(startUserIds) }} 可发起流程
|
||||
</el-text>
|
||||
<el-text v-else>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="getUserNicknames(startUserIds)"
|
||||
>
|
||||
{{ getUserNicknames(startUserIds.slice(0, 2)) }} 等
|
||||
{{ startUserIds.length }} 人可发起流程
|
||||
</el-tooltip>
|
||||
<el-text
|
||||
v-if="
|
||||
(!startUserIds || startUserIds.length === 0) &&
|
||||
(!startDeptIds || startDeptIds.length === 0)
|
||||
"
|
||||
>
|
||||
全部成员可以发起流程
|
||||
</el-text>
|
||||
<div v-else-if="startUserIds && startUserIds.length > 0">
|
||||
<el-text v-if="startUserIds.length == 1">
|
||||
{{ getUserNicknames(startUserIds) }} 可发起流程
|
||||
</el-text>
|
||||
<el-text v-else>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="getUserNicknames(startUserIds)"
|
||||
>
|
||||
{{ getUserNicknames(startUserIds.slice(0, 2)) }} 等
|
||||
{{ startUserIds.length }} 人可发起流程
|
||||
</el-tooltip>
|
||||
</el-text>
|
||||
</div>
|
||||
<div v-else-if="startDeptIds && startDeptIds.length > 0">
|
||||
<el-text v-if="startDeptIds.length == 1">
|
||||
{{ getDeptNames(startDeptIds) }} 可发起流程
|
||||
</el-text>
|
||||
<el-text v-else>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="getDeptNames(startDeptIds)"
|
||||
>
|
||||
{{ getDeptNames(startDeptIds.slice(0, 2)) }} 等
|
||||
{{ startDeptIds.length }} 个部门可发起流程
|
||||
</el-tooltip>
|
||||
</el-text>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
|
||||
<div class="field-setting-pane">
|
||||
@@ -107,6 +132,7 @@
|
||||
import { SimpleFlowNode, NodeType, FieldPermissionType, START_USER_BUTTON_SETTING } from '../consts'
|
||||
import { useWatchNode, useDrawer, useNodeName, useFormFieldsPermission } from '../node'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
defineOptions({
|
||||
name: 'StartUserNodeConfig'
|
||||
})
|
||||
@@ -118,8 +144,12 @@ const props = defineProps({
|
||||
})
|
||||
// 可发起流程的用户编号
|
||||
const startUserIds = inject<Ref<any[]>>('startUserIds')
|
||||
// 可发起流程的部门编号
|
||||
const startDeptIds = inject<Ref<any[]>>('startDeptIds')
|
||||
// 用户列表
|
||||
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList')
|
||||
// 部门列表
|
||||
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList')
|
||||
// 抽屉配置
|
||||
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||
// 当前节点
|
||||
@@ -145,6 +175,19 @@ const getUserNicknames = (userIds: number[]): string => {
|
||||
})
|
||||
return nicknames.join(',')
|
||||
}
|
||||
const getDeptNames = (deptIds: number[]): string => {
|
||||
if (!deptIds || deptIds.length === 0) {
|
||||
return ''
|
||||
}
|
||||
const deptNames: string[] = []
|
||||
deptIds.forEach((deptId) => {
|
||||
const found = deptOptions?.value.find((item) => item.id === deptId)
|
||||
if (found && found.name) {
|
||||
deptNames.push(found.name)
|
||||
}
|
||||
})
|
||||
return deptNames.join(',')
|
||||
}
|
||||
// 保存配置
|
||||
const saveConfig = async () => {
|
||||
activeTabName.value = 'user'
|
||||
|
||||
@@ -254,6 +254,7 @@ import {
|
||||
import { useWatchNode, useDrawer, useNodeName, useFormFields, getConditionShowText } from '../node'
|
||||
import HttpRequestSetting from './components/HttpRequestSetting.vue'
|
||||
import ConditionDialog from './components/ConditionDialog.vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
defineOptions({
|
||||
@@ -290,7 +291,7 @@ const configForm = ref<TriggerSetting>({
|
||||
},
|
||||
formSettings: [
|
||||
{
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
|
||||
updateFormFields: {},
|
||||
deleteFields: []
|
||||
}
|
||||
@@ -346,7 +347,7 @@ const changeTriggerType = () => {
|
||||
? originalSetting.formSettings
|
||||
: [
|
||||
{
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
|
||||
updateFormFields: {},
|
||||
deleteFields: []
|
||||
}
|
||||
@@ -361,7 +362,7 @@ const changeTriggerType = () => {
|
||||
? originalSetting.formSettings
|
||||
: [
|
||||
{
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
|
||||
updateFormFields: undefined,
|
||||
deleteFields: []
|
||||
}
|
||||
@@ -374,7 +375,7 @@ const changeTriggerType = () => {
|
||||
/** 添加新的修改表单设置 */
|
||||
const addFormSetting = () => {
|
||||
configForm.value.formSettings!.push({
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
|
||||
updateFormFields: {},
|
||||
deleteFields: []
|
||||
})
|
||||
@@ -509,7 +510,7 @@ const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
||||
},
|
||||
formSettings: node.triggerSetting.formSettings || [
|
||||
{
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE),
|
||||
updateFormFields: {},
|
||||
deleteFields: []
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@ import {
|
||||
} from '../../consts'
|
||||
import { BpmModelFormType } from '@/utils/constants'
|
||||
import { useFormFieldsAndStartUser } from '../../node'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -196,7 +197,7 @@ const formRef = ref() // 表单 Ref
|
||||
const changeConditionType = () => {
|
||||
if (condition.value.conditionType === ConditionType.RULE) {
|
||||
if (!condition.value.conditionGroups) {
|
||||
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
|
||||
condition.value.conditionGroups = cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- TODO @jason:有可能,它里面套 Condition 么? -->
|
||||
<!-- TODO 怕影响其它节点功能,后面看看如何如何复用 Condtion -->
|
||||
<!-- TODO 怕影响其它节点功能,后面看看如何如何复用 Condtion -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="条件配置" width="600px" :fullscreen="false">
|
||||
<div class="h-410px">
|
||||
@@ -165,6 +165,7 @@ import {
|
||||
} from '../../consts'
|
||||
import { BpmModelFormType } from '@/utils/constants'
|
||||
import { useFormFieldsAndStartUser } from '../../node'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
defineOptions({
|
||||
name: 'ConditionDialog'
|
||||
})
|
||||
@@ -175,7 +176,7 @@ const condition = ref<{
|
||||
conditionGroups?: ConditionGroup
|
||||
}>({
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -210,7 +211,7 @@ const formRef = ref() // 表单 Ref
|
||||
const changeConditionType = () => {
|
||||
if (condition.value.conditionType === ConditionType.RULE) {
|
||||
if (!condition.value.conditionGroups) {
|
||||
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
|
||||
condition.value.conditionGroups = cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,18 @@
|
||||
<script setup lang="ts">
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import {
|
||||
SimpleFlowNode,
|
||||
NodeType,
|
||||
ConditionType,
|
||||
DEFAULT_CONDITION_GROUP_VALUE,
|
||||
NODE_DEFAULT_TEXT
|
||||
} from '../consts'
|
||||
import { getDefaultConditionNodeName } from '../utils'
|
||||
import { useTaskStatusClass } from '../node'
|
||||
import { generateUUID } from '@/utils'
|
||||
import ConditionNodeConfig from '../nodes-config/ConditionNodeConfig.vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
defineOptions({
|
||||
name: 'ExclusiveNode'
|
||||
@@ -149,7 +156,8 @@ const blurEvent = (index: number) => {
|
||||
showInputs.value[index] = false
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||
conditionNode.name =
|
||||
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
conditionNode.name ||
|
||||
getDefaultConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
}
|
||||
|
||||
// 点击条件名称
|
||||
@@ -181,7 +189,7 @@ const addCondition = () => {
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
}
|
||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||
|
||||
@@ -110,11 +110,18 @@
|
||||
<script setup lang="ts">
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import {
|
||||
SimpleFlowNode,
|
||||
NodeType,
|
||||
ConditionType,
|
||||
DEFAULT_CONDITION_GROUP_VALUE,
|
||||
NODE_DEFAULT_TEXT
|
||||
} from '../consts'
|
||||
import { useTaskStatusClass } from '../node'
|
||||
import { getDefaultInclusiveConditionNodeName } from '../utils'
|
||||
import { generateUUID } from '@/utils'
|
||||
import ConditionNodeConfig from '../nodes-config/ConditionNodeConfig.vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
defineOptions({
|
||||
name: 'InclusiveNode'
|
||||
@@ -153,7 +160,8 @@ const blurEvent = (index: number) => {
|
||||
showInputs.value[index] = false
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||
conditionNode.name =
|
||||
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
conditionNode.name ||
|
||||
getDefaultInclusiveConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
}
|
||||
|
||||
// 点击条件名称
|
||||
@@ -185,7 +193,7 @@ const addCondition = () => {
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
conditionGroups: cloneDeep(DEFAULT_CONDITION_GROUP_VALUE)
|
||||
}
|
||||
}
|
||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||
|
||||
63
src/components/Tinyflow/Tinyflow.vue
Normal file
63
src/components/Tinyflow/Tinyflow.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div ref="divRef" :class="['tinyflow', className]" :style="style" style="height: 100%"> </div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Item, Tinyflow as TinyflowNative } from './ui'
|
||||
import './ui/index.css'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
className?: string
|
||||
style?: Record<string, string>
|
||||
data?: Record<string, any>
|
||||
provider?: {
|
||||
llm?: () => Item[] | Promise<Item[]>
|
||||
knowledge?: () => Item[] | Promise<Item[]>
|
||||
internal?: () => Item[] | Promise<Item[]>
|
||||
}
|
||||
}>()
|
||||
|
||||
const divRef = ref<HTMLDivElement | null>(null)
|
||||
let tinyflow: TinyflowNative | null = null
|
||||
// 定义默认的 provider 方法
|
||||
const defaultProvider = {
|
||||
llm: () => [] as Item[],
|
||||
knowledge: () => [] as Item[],
|
||||
internal: () => [] as Item[]
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (divRef.value) {
|
||||
// 合并默认 provider 和传入的 props.provider
|
||||
const mergedProvider = {
|
||||
...defaultProvider,
|
||||
...props.provider
|
||||
}
|
||||
tinyflow = new TinyflowNative({
|
||||
element: divRef.value as Element,
|
||||
data: props.data || {},
|
||||
provider: mergedProvider
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (tinyflow) {
|
||||
tinyflow.destroy()
|
||||
tinyflow = null
|
||||
}
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
if (tinyflow) {
|
||||
return tinyflow.getData()
|
||||
}
|
||||
console.warn('Tinyflow instance is not initialized')
|
||||
return null
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getData
|
||||
})
|
||||
</script>
|
||||
1
src/components/Tinyflow/ui/index.css
Normal file
1
src/components/Tinyflow/ui/index.css
Normal file
File diff suppressed because one or more lines are too long
41
src/components/Tinyflow/ui/index.d.ts
vendored
Normal file
41
src/components/Tinyflow/ui/index.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Edge } from '@xyflow/svelte';
|
||||
import { Node as Node_2 } from '@xyflow/svelte';
|
||||
import { useSvelteFlow } from '@xyflow/svelte';
|
||||
import { Viewport } from '@xyflow/svelte';
|
||||
|
||||
export declare type Item = {
|
||||
value: number | string;
|
||||
label: string;
|
||||
children?: Item[];
|
||||
};
|
||||
|
||||
export declare class Tinyflow {
|
||||
private options;
|
||||
private rootEl;
|
||||
private svelteFlowInstance;
|
||||
constructor(options: TinyflowOptions);
|
||||
private _init;
|
||||
private _setOptions;
|
||||
getOptions(): TinyflowOptions;
|
||||
getData(): {
|
||||
nodes: Node_2[];
|
||||
edges: Edge[];
|
||||
viewport: Viewport;
|
||||
};
|
||||
setData(data: TinyflowData): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export declare type TinyflowData = Partial<ReturnType<ReturnType<typeof useSvelteFlow>['toObject']>>;
|
||||
|
||||
export declare type TinyflowOptions = {
|
||||
element: string | Element;
|
||||
data?: TinyflowData;
|
||||
provider?: {
|
||||
llm?: () => Item[] | Promise<Item[]>;
|
||||
knowledge?: () => Item[] | Promise<Item[]>;
|
||||
internal?: () => Item[] | Promise<Item[]>;
|
||||
};
|
||||
};
|
||||
|
||||
export { }
|
||||
16984
src/components/Tinyflow/ui/index.js
Normal file
16984
src/components/Tinyflow/ui/index.js
Normal file
File diff suppressed because it is too large
Load Diff
9
src/components/Tinyflow/ui/index.umd.js
Normal file
9
src/components/Tinyflow/ui/index.umd.js
Normal file
File diff suppressed because one or more lines are too long
@@ -86,7 +86,8 @@ const props = defineProps({
|
||||
autoUpload: propTypes.bool.def(true), // 自动上传
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
isShowTip: propTypes.bool.def(true), // 是否显示提示
|
||||
disabled: propTypes.bool.def(false) // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
directory: propTypes.string.def(undefined) // 上传目录 ==> 非必传(默认为 undefined)
|
||||
})
|
||||
|
||||
// ========== 上传相关 ==========
|
||||
@@ -95,7 +96,7 @@ const uploadList = ref<UploadUserFile[]>([])
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
const { uploadUrl, httpRequest } = useUpload(props.directory)
|
||||
|
||||
// 文件上传之前判断
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
|
||||
@@ -79,7 +79,8 @@ const props = defineProps({
|
||||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
showDelete: propTypes.bool.def(true), // 是否显示删除按钮
|
||||
showBtnText: propTypes.bool.def(true) // 是否显示按钮文字
|
||||
showBtnText: propTypes.bool.def(true), // 是否显示按钮文字
|
||||
directory: propTypes.string.def(undefined) // 上传目录 ==> 非必传(默认为 undefined)
|
||||
})
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -99,7 +100,7 @@ const deleteImg = () => {
|
||||
emit('update:modelValue', '')
|
||||
}
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
const { uploadUrl, httpRequest } = useUpload(props.directory)
|
||||
|
||||
const editImg = () => {
|
||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
||||
|
||||
@@ -81,10 +81,11 @@ const props = defineProps({
|
||||
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
||||
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
||||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||
borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
directory: propTypes.string.def(undefined) // 上传目录 ==> 非必传(默认为 undefined)
|
||||
})
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
const { uploadUrl, httpRequest } = useUpload(props.directory)
|
||||
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import CryptoJS from 'crypto-js'
|
||||
// import CryptoJS from 'crypto-js'
|
||||
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||
import axios from 'axios'
|
||||
|
||||
@@ -10,7 +10,7 @@ export const getUploadUrl = (): string => {
|
||||
return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/infra/file/upload'
|
||||
}
|
||||
|
||||
export const useUpload = () => {
|
||||
export const useUpload = (directory?: string) => {
|
||||
// 后端上传地址
|
||||
const uploadUrl = getUploadUrl()
|
||||
// 是否使用前端直连上传
|
||||
@@ -22,7 +22,7 @@ export const useUpload = () => {
|
||||
// 1.1 生成文件名称
|
||||
const fileName = await generateFileName(options.file)
|
||||
// 1.2 获取文件预签名地址
|
||||
const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
|
||||
const presignedInfo = await FileApi.getFilePresignedUrl(fileName, directory)
|
||||
// 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
|
||||
return axios
|
||||
.put(presignedInfo.uploadUrl, options.file, {
|
||||
@@ -32,7 +32,7 @@ export const useUpload = () => {
|
||||
})
|
||||
.then(() => {
|
||||
// 1.4. 记录文件信息到后端(异步)
|
||||
createFile(presignedInfo, fileName, options.file)
|
||||
createFile(presignedInfo, options.file)
|
||||
// 通知成功,数据格式保持与后端上传的返回结果一致
|
||||
return { data: presignedInfo.url }
|
||||
})
|
||||
@@ -40,7 +40,7 @@ export const useUpload = () => {
|
||||
// 模式二:后端上传
|
||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||
return new Promise((resolve, reject) => {
|
||||
FileApi.updateFile({ file: options.file })
|
||||
FileApi.updateFile({ file: options.file, directory })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve(res)
|
||||
@@ -67,11 +67,11 @@ export const useUpload = () => {
|
||||
* @param name 文件名称
|
||||
* @param file 文件
|
||||
*/
|
||||
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
|
||||
function createFile(vo: FileApi.FilePresignedUrlRespVO, file: UploadRawFile) {
|
||||
const fileVo = {
|
||||
configId: vo.configId,
|
||||
url: vo.url,
|
||||
path: name,
|
||||
path: vo.path,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size
|
||||
@@ -85,14 +85,15 @@ function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: Uplo
|
||||
* @param file 要上传的文件
|
||||
*/
|
||||
async function generateFileName(file: UploadRawFile) {
|
||||
// 读取文件内容
|
||||
const data = await file.arrayBuffer()
|
||||
const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||
// 计算SHA256
|
||||
const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||
// 拼接后缀
|
||||
const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||
return `${sha256}${ext}`
|
||||
// // 读取文件内容
|
||||
// const data = await file.arrayBuffer()
|
||||
// const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||
// // 计算SHA256
|
||||
// const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||
// // 拼接后缀
|
||||
// const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||
// return `${sha256}${ext}`
|
||||
return file.name
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -237,7 +237,7 @@ const props = defineProps({
|
||||
const prefix = inject('prefix')
|
||||
const width = inject('width')
|
||||
|
||||
const formKey = ref('')
|
||||
const formKey = ref(undefined)
|
||||
const businessKey = ref('')
|
||||
const optionModelTitle = ref('')
|
||||
const fieldList = ref<any[]>([])
|
||||
@@ -462,6 +462,7 @@ const updateElementExtensions = () => {
|
||||
const formList = ref([]) // 流程表单的下拉框的数据
|
||||
onMounted(async () => {
|
||||
formList.value = await FormApi.getFormSimpleList()
|
||||
formKey.value = parseInt(formKey.value)
|
||||
})
|
||||
|
||||
watch(
|
||||
|
||||
@@ -370,7 +370,6 @@ const removeListenerField = (index) => {
|
||||
}
|
||||
// 移除监听器
|
||||
const removeListener = (index) => {
|
||||
debugger
|
||||
ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
|
||||
confirmButtonText: '确 认',
|
||||
cancelButtonText: '取 消'
|
||||
|
||||
@@ -2,7 +2,6 @@ import { toRaw } from 'vue'
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||
// 创建监听器实例
|
||||
export function createListenerObject(options, isTask, prefix) {
|
||||
debugger
|
||||
const listenerObj = Object.create(null)
|
||||
listenerObj.event = options.event
|
||||
isTask && (listenerObj.id = options.id) // 任务监听器特有的 id 字段
|
||||
|
||||
Reference in New Issue
Block a user