diff --git a/bk.config.js b/bk.config.js index 98475e05c..cc0d0916e 100644 --- a/bk.config.js +++ b/bk.config.js @@ -45,7 +45,8 @@ module.exports = { VueI18n: path.resolve(__dirname, './node_modules/vue-i18n') }, fallback: { - buffer: require.resolve('buffer') + buffer: require.resolve('buffer'), + vm: require.resolve('vm-browserify') } }, devServer: { diff --git a/lib/client/src/api/pureAxios.js b/lib/client/src/api/pureAxios.js index c44412bf7..5a16e3b2b 100644 --- a/lib/client/src/api/pureAxios.js +++ b/lib/client/src/api/pureAxios.js @@ -105,7 +105,11 @@ async function getPromise (method, url, data, userConfig = {}) { try { const response = await axiosRequest Object.assign(config, response.config || {}) - handleResponse({ config, response, resolve, reject }) + if (Object.prototype.hasOwnProperty.call(response, 'code') && response.code !== 0) { + reject(response) + } else { + handleResponse({ config, response, resolve, reject }) + } } catch (error) { Object.assign(config, error.config) reject(error) diff --git a/lib/client/src/common/ai.js b/lib/client/src/common/ai.js index 00c254d36..d5b01f249 100644 --- a/lib/client/src/common/ai.js +++ b/lib/client/src/common/ai.js @@ -56,8 +56,8 @@ export class Ai { this.pushPrompt(this.systemPrompt, 'system') } - receiveMessage (message) { - this.content += message + receiveMessage (message, cover) { + this.content = cover ? message : this.content + message const cmdIndex = this.content.indexOf('``') const aiMessage = cmdIndex > 0 ? this.content.slice(0, cmdIndex).trim() : this.content // 触发消息更新 @@ -65,6 +65,10 @@ export class Ai { } async endReceiveMessage () { + if (!this.content.trim()) { + this.handleApiError('模型调用失败,返回空文本') + return + } this.pushPrompt(this.content, 'assistant') // 触发指令 const cmdIndex = this.content.indexOf('``') @@ -127,27 +131,44 @@ export class Ai { while (true) { try { const { value, done } = await reader.read() - if (done) break - const values = (temp + value.toString()).split(/(?<=})\s*(?={"result")/) + // 接口异常处理 + if (!response.ok) { + this.handleApiError(value || response.statusText) + break + } + // 接口完成 + if (done) { + this.endReceiveMessage() + break + } + const values = (temp + value.toString()).split('\n') values.forEach((value) => { - const item = value.trim() + const item = value.replace('data:', '').trim() if (isJSON(item)) { const { + event, + content, + cover, result, - data, message } = JSON.parse(item) - if (result === true) { - const choice = data?.result?.choices?.[0] - if (!choice) { - this.handleApiError('模型调用失败,返回对象为空') - } else if (choice.finish_reason) { - this.endReceiveMessage() - } else if (choice.delta.content) { - this.receiveMessage(choice.delta.content) - } - } else { + + // 业务错误处理 + if (result === false || response.status !== 200) { this.handleApiError(message || '模型调用失败') + return + } + + switch (event) { + case 'text': + this.receiveMessage(content, cover) + break + case 'done': + this.endReceiveMessage() + break + case 'error': + this.handleApiError(message || '模型调用失败') + break } temp = '' } else if (item) { @@ -182,7 +203,7 @@ export class Ai { if (!choice) { this.handleApiError('模型调用失败,返回对象为空') } else { - this.receiveMessage(choice.message.content) + this.receiveMessage(choice.delta.content) this.endReceiveMessage() } } else { diff --git a/lib/client/src/common/util.js b/lib/client/src/common/util.js index 9269ddaa6..cb42167b2 100644 --- a/lib/client/src/common/util.js +++ b/lib/client/src/common/util.js @@ -11,6 +11,7 @@ import { messageSuccess } from '@/common/bkmagic' import domToImage from './dom-to-image' +import DOMPurify from 'dompurify' import Vue from 'vue' import LC from '@/element-materials/core' import store from '@/store' @@ -1025,3 +1026,8 @@ export const isJSON = (str) => { return false } } + +export const filterImgSrc = (src) => { + src?.replaceAll('console', '')?.replaceAll('logout', '') + return DOMPurify.sanitize(src) +} diff --git a/lib/client/src/components/methods/forms/form-items/children/debug/param.vue b/lib/client/src/components/methods/forms/form-items/children/debug/param.vue index e3e3d3578..f98838c01 100644 --- a/lib/client/src/components/methods/forms/form-items/children/debug/param.vue +++ b/lib/client/src/components/methods/forms/form-items/children/debug/param.vue @@ -98,7 +98,8 @@ break case 'array': case 'object': - renderVal = getValueFromString(val) + // renderVal = getValueFromString(val) + renderVal = val break case 'number': renderVal = Number(val) diff --git a/lib/client/src/components/project/create-page-dialog.vue b/lib/client/src/components/project/create-page-dialog.vue index 226591eeb..1a616bd2a 100644 --- a/lib/client/src/components/project/create-page-dialog.vue +++ b/lib/client/src/components/project/create-page-dialog.vue @@ -112,6 +112,10 @@ type: String, default: '' }, + useCustomSave: { + type: Boolean, + default: false + }, // form和flow的数据管理页需要填写默认的pageCode和pageName,带上formId或flowId initPageData: { type: Object, @@ -249,7 +253,7 @@ }, handleConfirmClick () { // 判断是不是流程页面或流程管理页面 - if (['FLOW', 'FLOW_MANAGE'].includes(this.nocodeType)) { + if (this.useCustomSave || ['FLOW', 'FLOW_MANAGE'].includes(this.nocodeType)) { this.$emit('save') } else { this.save() @@ -284,12 +288,7 @@ content.push(JSON.parse(template.content)) templateFormData = { previewImg: template.previewImg, content: JSON.stringify(content) } } - - const formData = { - pageName: this.formData.pageName, - pageCode: this.formData.pageCode - } - + const payload = { data: { pageData: Object.assign({}, this.formData, templateFormData), @@ -307,7 +306,7 @@ const res = await this.$store.dispatch('page/create', payload) if (res) { // 页面不是流程页面或流程管理页面 - if (!['FLOW', 'FLOW_MANAGE'].includes(this.nocodeType)) { + if (!this.useCustomSave && !['FLOW', 'FLOW_MANAGE'].includes(this.nocodeType)) { this.$bkMessage({ theme: 'success', message: this.$t('新建页面成功') diff --git a/lib/client/src/components/project/page-from-template.vue b/lib/client/src/components/project/page-from-template.vue index 523f21a0c..63081ee6c 100644 --- a/lib/client/src/components/project/page-from-template.vue +++ b/lib/client/src/components/project/page-from-template.vue @@ -73,6 +73,7 @@ import PreivewErrImg from '@/images/preview-error.png' import { PAGE_TEMPLATE_TYPE } from '@/common/constant' import TemplateEditDialog from '@/views/project/template-manage/components/template-edit-dialog' + import { filterImgSrc } from '@/common/util' export default { name: 'page-from-template', @@ -160,7 +161,7 @@ }, getPreviewImg (previewImg) { if (previewImg && previewImg.length > 20) { - return previewImg + return filterImgSrc(previewImg) } return PreivewErrImg }, diff --git a/lib/client/src/components/render/pc/render-component.js b/lib/client/src/components/render/pc/render-component.js index aa2e64ac5..473b1f8a5 100644 --- a/lib/client/src/components/render/pc/render-component.js +++ b/lib/client/src/components/render/pc/render-component.js @@ -19,6 +19,7 @@ import WidgetVanPicker from './widget/van-picker' import WidgetFormContainer from './widget/form-container' import WidgetDataManageContainer from './widget/data-manage-container/form-data-manage/edit/index' import WidgetFlowManageContainer from './widget/flow-manage-container/edit/index' +import WidgetFlowWorkbenchContainer from './widget/flow-workbench-container/edit/index' import WidgetVanTab from './widget/van-tab' import BkCharts from './widget/bk-charts/bk-charts' import Chart from './widget/chart/chart' @@ -53,6 +54,7 @@ export default { WidgetFormContainer, WidgetDataManageContainer, WidgetFlowManageContainer, + WidgetFlowWorkbenchContainer, WidgetVanTab, BkLuckyCanvas }, diff --git a/lib/client/src/components/render/pc/tools/lesscode-tool/hooks/use-check-form-engine.js b/lib/client/src/components/render/pc/tools/lesscode-tool/hooks/use-check-form-engine.js index eb6ddb391..6a6b46fda 100644 --- a/lib/client/src/components/render/pc/tools/lesscode-tool/hooks/use-check-form-engine.js +++ b/lib/client/src/components/render/pc/tools/lesscode-tool/hooks/use-check-form-engine.js @@ -1,3 +1,3 @@ export default function (type) { - return ['widget-form-container', 'widget-data-manage-container'].includes(type) + return ['widget-form-container', 'widget-data-manage-container', 'widget-flow-manage-container', 'widget-flow-workbench-container'].includes(type) } diff --git a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/edit/components/table/index.postcss b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/edit/components/table/index.postcss index f24612b54..a2b5d5465 100644 --- a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/edit/components/table/index.postcss +++ b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/edit/components/table/index.postcss @@ -3,9 +3,6 @@ /* v3 table 样式 */ .bk-lesscode-table { - .bk-lesscode-table-head table th.column_fixed { - background: inherit; - } .bk-lesscode-table-body { overflow: visible; } diff --git a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/components/table/index.js b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/components/table/index.js index d0c64afe3..eaf6255ec 100644 --- a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/components/table/index.js +++ b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/components/table/index.js @@ -181,9 +181,10 @@ export default { return h({ component: 'bk-table', props: { + border: framework === 'vue2' ? false : ['outer', 'row'], data: this.tableData, pagination: this.pagination, - remotePagination: true, + remotePagination: true }, on: { 'page-change': (val) => { @@ -193,6 +194,7 @@ export default { self.handlePageChange(val) }, 'page-limit-change': (val) => { + self.pagination.current = 1 self.handlePageLimitChange(val) } }, diff --git a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.js b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.js index 50f5ac31d..87474ce3c 100644 --- a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.js +++ b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.js @@ -69,7 +69,7 @@ export default { component: 'div', class: 'widget-data-manage-container', children: this.loading ? null : [ - h({ + (this.buttons.length > 0 || this.filters.length > 0) ? h({ component: 'div', class: 'data-manage-operate-area', children: [ @@ -97,7 +97,7 @@ export default { ] }) : null ] - }), + }) : null, (this.hideFilters || this.filters.length === 0) ? null : h({ component: filters, props: { diff --git a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.postcss b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.postcss index 69560815a..6cc449154 100644 --- a/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.postcss +++ b/lib/client/src/components/render/pc/widget/data-manage-container/form-data-manage/preview/index.postcss @@ -27,4 +27,10 @@ color: #3a84ff; } } + .bk-lesscode-table { + border-bottom: 1px solid #dcdee5; + .bk-lesscode-table-body tbody tr td { + border-bottom-color: #dcdee5; + } + } } diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/edit/flow-manage-container.postcss b/lib/client/src/components/render/pc/widget/flow-manage-container/edit/flow-manage-container.postcss index 217443933..c5953efda 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/edit/flow-manage-container.postcss +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/edit/flow-manage-container.postcss @@ -4,6 +4,10 @@ background: #FFFFFF; border: 1px dashed #CCCCCC; overflow: hidden; + .flow-manage-title { + font-size: 16px; + color: #313238; + } &.empty{ padding: 24px 32px; background: #F9F8FC; @@ -28,7 +32,8 @@ pointer-events: auto !important; } } - .process-render { + .process-render * { + pointer-events: auto !important; .filter-render-wrapper { padding: 16px 24px; background: #FAFBFC; @@ -120,4 +125,10 @@ } } } + .bk-lesscode-table { + border-bottom: 1px solid #dcdee5; + .bk-lesscode-table-body tbody tr td { + border-bottom-color: #dcdee5; + } + } } diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/edit/index.js b/lib/client/src/components/render/pc/widget/flow-manage-container/edit/index.js index 4026edfe5..0de18a064 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/edit/index.js +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/edit/index.js @@ -1,10 +1,11 @@ import { h } from 'bk-lesscode-render' import cloneDeep from 'lodash.clonedeep' +import http from '@/api/pureAxios' +import LC from '@/element-materials/core' +import processOverview from '../preview/components/process-overview' import formDataTable from '../../data-manage-container/form-data-manage/edit/components/table' import tableFilters from '../../data-manage-container/form-data-manage/edit/components/table-filters' -import LC from '@/element-materials/core' import formDataButtons from '../../data-manage-container/form-data-manage/edit/components/form-data-buttons' -import http from '@/api/pureAxios' import { FORM_SYS_FIELD, FIELDS_NOT_DISPLAYED_IN_TABLE } from '../../data-manage-container/form-data-manage/constants' import './flow-manage-container.postcss' @@ -20,6 +21,7 @@ export default { data () { return { loading: false, + tplName: '', activeTab: 'process', nodeDataList: [], // 节点数据节点列表 activeNode: '', @@ -86,6 +88,8 @@ export default { // 将 currentProps 转换为一个 Map,以便快速查找 const currentPropsMap = new Map(currentProps.map(node => [node.id, node])) + this.tplName = data.name + // 将后端返回的节点数据与当前组件的 props 数据进行合并,以便能够正确的展示人工节点的配置信息 this.nodeDataList = manualNodes.map(node => { const currentNodeProps = currentPropsMap.get(node.id) || {} @@ -102,7 +106,7 @@ export default { ...currentNodeProps } }) - + // 如果当前组件的 props 数据中没有人工节点数据,则将第一个节点的 id 设置为 activeNode this.activeNode = this.nodeDataList[0]?.id || '' this.loading = false @@ -229,91 +233,16 @@ export default { }) } - const filterFormItem = [ - { label: this.$t('创建人'), component: 'bk-input', placeholder: this.$t('请输入创建人') }, - { label: this.$t('创建时间'), component: 'bk-input', placeholder: this.$t('请输入创建时间') }, - { label: this.$t('状态'), component: 'bk-select', placeholder: this.$t('请选择状态') } - ] - const processRender = () => h({ component: 'div', class: 'process-render', children: [ h({ - component: 'div', - class: 'filter-render-wrapper', - children: [ - h({ - component: 'bk-form', - props: { - 'form-type': 'vertical' - }, - children: filterFormItem.map((item, index) => - h({ - component: 'bk-form-item', - props: { - label: item.label, - extCls: index === 0 ? '' : 'ml10', - class: index === 0 ? '' : 'ml10' - }, - children: [ - h({ - component: item.component, - props: { - placeholder: item.placeholder - } - }) - ] - }) - ) - }), - h({ - component: 'div', - class: 'filter-btn-area mt20', - children: [ - h({ - component: 'bk-button', - props: { - theme: 'primary' - }, - children: [this.$t('查询')] - }), - h({ - component: 'bk-button', - class: 'ml10', - props: { - theme: 'default' - }, - children: [this.$t('重置')] - }) - ] - }) - ] - }), - h({ - component: 'bk-table', - class: 'mt20', + component: processOverview, props: { - data: [] - }, - children: [ - ...filterFormItem.map((item, index) => - h({ - component: 'bk-table-column', - props: { - label: item.label, - index: index - } - }) - ), - h({ - component: 'bk-table-column', - props: { - label: self.$t('操作') - } - }) - ] + id: self.propsData.id + } }) ] }) @@ -361,6 +290,11 @@ export default { const getElements = () => { return [ + h({ + component: 'div', + class: 'flow-manage-title', + children: [`${this.tplName}流程管理`] + }), h({ component: 'bk-tab', props: { diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.js b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.js index 543bd6a3d..b899da150 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.js +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.js @@ -8,14 +8,21 @@ import './process-overview.postcss' export default { props: { + // 是否为流程工作台容器,流程工作台容器展示项目下所有任务 + isWorkbench: { + type: Boolean, + default: false + }, + // 指定流程模板,流程管理容器需要 id: { type: String, - required: true + required: false } }, data () { return { formData: { + tplName: '', createUser: '', time: [], id: '' @@ -56,7 +63,9 @@ export default { } }) this.loading = true - const res = await http.get(`/flow/tpl/${this.id}/task/list`, { params: { + + const url = this.isWorkbench ? '/flow/task/list' : `/flow/tpl/${this.id}/task/list` + const res = await http.get(url, { params: { page: this.pagination.current, pageSize: this.pagination.limit, ...query @@ -72,12 +81,12 @@ export default { const filterFormItem = [ { - label: this.$t('id'), + label: this.$t('任务ID'), component: 'bk-input', property: 'id', name: 'id', props: { - placeholder: this.$t('请输入id'), + placeholder: this.$t('请输入任务ID'), ...(framework === 'vue3' ? { modelValue: this.formData.id } : { value: this.formData.id }) }, on: framework === 'vue3' ? { @@ -96,7 +105,7 @@ export default { property: 'createUser', name: 'createUser', props: { - placeholder: this.$t('请输入创建时间'), + placeholder: this.$t('请输入创建人'), ...(framework === 'vue3' ? { modelValue: this.formData.createUser } : { value: this.formData.createUser }) }, on: framework === 'vue3' ? { @@ -134,6 +143,42 @@ export default { } } }, + { + label: this.$t('执行人'), + component: 'bk-input', + property: 'executor', + name: 'createUser', + props: {}, + on: framework === 'vue3' ? { + 'update:modelValue': (value) => { + self.formData.createUser = value + } + } : { + 'input': (value) => { + self.formData.createUser = value + } + } + }, + { + label: this.$t('执行时间'), + component: 'bk-date-picker', + property: 'startTime', + name: 'time', + props: {}, + on: { + 'change': (value) => { + self.formData.time = value + } + }, + scopedSlots: { + default: ({ row }) => { + return h({ + component: 'div', + children: [dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss')] + }) + } + } + }, { label: this.$t('状态'), component: 'bk-select', @@ -154,7 +199,7 @@ export default { }, scopedSlots: { default: ({ row }) => { - return h({ + return row.status ? h({ component: 'div', class: 'flow-status-item', children: [h({ @@ -165,7 +210,7 @@ export default { class: 'flow-status-text', children: [statusMap[row.status]] })] - }) + }) : '--' } }, children: self.statusList.map(status => (h({ @@ -180,6 +225,28 @@ export default { } ] + if (this.isWorkbench) { + filterFormItem.splice(1, 0, { + label: this.$t('流程'), + component: 'bk-input', + property: 'tplName', + name: 'tpl.name', + props: { + placeholder: this.$t('请输入流程名称'), + ...(framework === 'vue3' ? { modelValue: this.formData.tplName } : { value: this.formData.tplName }) + }, + on: framework === 'vue3' ? { + 'update:modelValue': (value) => { + self.formData.tplName = value + } + } : { + 'input': (value) => { + self.formData.tplName = value + } + } + }) + } + return h({ component: 'div', class: 'process-render', @@ -194,30 +261,26 @@ export default { 'form-type': 'vertical', 'model': this.formData }, - children: filterFormItem.map((item, index) => - item.property === 'status' - ? null - : h({ - component: 'bk-form-item', - props: { - label: item.label, - extCls: index === 0 ? '' : 'ml10', - class: index === 0 ? '' : 'ml10', - property: item.property + children: filterFormItem.filter((item) => !['executor', 'startTime', 'status'].includes(item.property)).map((item, index) => h({ + component: 'bk-form-item', + props: { + label: item.label, + extCls: index === 0 ? '' : 'ml10', + class: index === 0 ? '' : 'ml10', + property: item.property + }, + children: [ + h({ + component: item.component, + props: item.props, + style: { + width: '100%' }, - children: [ - h({ - component: item.component, - props: item.props, - style: { - width: '100%' - }, - children: item.children ? item.children : [], - on: item.on - }) - ] + children: item.children ? item.children : [], + on: item.on }) - ) + ] + })) }), h({ component: 'div', @@ -231,6 +294,7 @@ export default { children: [this.$t('查询')], on: { click: () => { + self.pagination.current = 1 self.getTableData() } } @@ -245,10 +309,12 @@ export default { on: { click: () => { self.formData = { + tplName: '', createUser: '', time: [], id: '' } + self.pagination.current = 1 self.getTableData() } } @@ -261,6 +327,7 @@ export default { component: 'bk-table', class: 'mt20', props: { + border: framework === 'vue2' ? false : ['outer'], data: self.tableData, pagination: self.pagination, remotePagination: true @@ -271,6 +338,7 @@ export default { self.getTableData() }, 'page-limit-change': (limit) => { + self.pagination.current = 1 self.pagination.limit = limit self.getTableData() } @@ -280,6 +348,7 @@ export default { self.getTableData() }, 'page-limit-change': (limit) => { + self.pagination.current = 1 self.pagination.limit = limit self.getTableData() } diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.postcss b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.postcss index 4bebedf61..0b20a1138 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.postcss +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/components/process-overview.postcss @@ -1,4 +1,6 @@ .process-render { + background: #ffffff; + .filter-render-wrapper { padding: 16px 24px; background: #FAFBFC; @@ -9,6 +11,7 @@ .bk-form-item, .bk-lesscode-form-item { flex: 1; margin-top: 0 !important; + margin-bottom: 0 !important; } } .edit-text { diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.js b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.js index 76113959e..d55d87387 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.js +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.js @@ -22,6 +22,7 @@ export default { data () { return { loading: false, + tplName: '', activeTab: 'process', activeNode: '', nodeDataList: [] @@ -32,7 +33,7 @@ export default { return this.nodeDataList.find(item => item.id === this.activeNode) }, allFields () { - const list = this.currentNodeTab?.fields.map(item => Object.assign({}, item, { system: false })) + const list = this.currentNodeTab?.fields.map(item => Object.assign({}, item, { system: false })) || [] FORM_SYS_FIELD.forEach(item => { const { id, name, key, type } = item list.push({ id, type, system: true, configure: { key, name } }) @@ -84,7 +85,8 @@ export default { ...currentNodeProps } }) - + + this.tplName = data.name // 如果当前组件的 props 数据中没有人工节点数据,则将第一个节点的 id 设置为 activeNode if (this.nodeDataList) { if (!this.activeNode) { @@ -165,9 +167,9 @@ export default { const renderFilters = () => h({ component: filters, props: { - fields: this.allFields, - filters: this.currentNodeTab?.filters, - queryData: self.currentNodeTab.queryData + fields: self.allFields, + filters: self.currentNodeTab?.filters, + queryData: self.currentNodeTab?.queryData }, on: { query: (val) => { @@ -180,10 +182,10 @@ export default { component: table, props: { fields: this.allFields, - queryData: self.currentNodeTab.queryData, - tableName: self.currentNodeTab.tableName, - tableRowActions: self.currentNodeTab.tableRowActions, - tableColsExclude: self.currentNodeTab.tableColsExclude + queryData: self.currentNodeTab?.queryData, + tableName: self.currentNodeTab?.tableName, + tableRowActions: self.currentNodeTab?.tableRowActions, + tableColsExclude: self.currentNodeTab?.tableColsExclude } }) @@ -234,7 +236,7 @@ export default { ) }), renderOperateArea(), - this.currentNodeTab?.hideFilters ? '' : renderFilters(), + (this.currentNodeTab && !this.currentNodeTab.hideFilters && this.currentNodeTab.filters.length > 0) ? renderFilters() : '', this.currentNodeTab?.tableName ? renderTable() : '' ] }) @@ -243,6 +245,11 @@ export default { const getElements = () => { return [ + h({ + component: 'div', + class: 'flow-manage-title', + children: [`${self.tplName}流程管理`] + }), h({ component: 'bk-tab', props: { diff --git a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.postcss b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.postcss index eec557833..1d01a4fbb 100644 --- a/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.postcss +++ b/lib/client/src/components/render/pc/widget/flow-manage-container/preview/index.postcss @@ -1,58 +1,66 @@ -.node-tab-wrapper { - .bk-tab, .bk-lesscode-tab { - padding: 4px; - max-width: calc(100% - 250px); - background: #f0f1f5; - border-radius: 2px; - margin-bottom: 20px; - .bk-tab-section { - display: none; - } - .bk-tab-header { +.flow-manage-wrapper { + padding: 20px 24px; + background: #ffffff; + .flow-manage-title { + font-size: 16px; + color: #313238; + } + .node-tab-wrapper { + .bk-tab, .bk-lesscode-tab { + padding: 4px; + max-width: calc(100% - 250px); background: #f0f1f5; - background-image: none !important; - height: 25px !important; - border: none; - .bk-tab-label-list { - height: 100% !important; - display: flex; - align-items: center; - + border-radius: 2px; + margin-bottom: 20px; + .bk-tab-section { + display: none; } - .bk-tab-label { - font-size: 12px; + .bk-tab-header { + background: #f0f1f5; + background-image: none !important; + height: 25px !important; + border: none; + .bk-tab-label-list { + height: 100% !important; + display: flex; + align-items: center; + + } + .bk-tab-label { + font-size: 12px; + } + .bk-tab-label-item { + border: none !important; + line-height: 25px; + font-size: 12px; + } } - .bk-tab-label-item { - border: none !important; - line-height: 25px; - font-size: 12px; + .bk-tab-label-wrapper { + .bk-tab-scroll-controller { + height: 33px !important; + background: #f5f7fa; + color: #979ba5; + &:hover { + background: #eaebf0; + color: #63656e; + } + } } } - .bk-tab-label-wrapper { - .bk-tab-scroll-controller { - height: 33px !important; - background: #f5f7fa; - color: #979ba5; - &:hover { - background: #eaebf0; - color: #63656e; + .bk-lesscode-tab { + .bk-lesscode-tab-header { + line-height: 20px !important; + padding: 4px !important; + border-bottom: 0px !important; + font-size: 12px; + .bk-lesscode-tab-header-item.bk-lesscode-tab-header--active { + border-radius: 2px 2px 0 0 ; } } - } - } - .bk-lesscode-tab { - .bk-lesscode-tab-header { - line-height: 20px !important; - padding: 4px !important; - border-bottom: 0px !important; - font-size: 12px; - .bk-lesscode-tab-header-item.bk-lesscode-tab-header--active { - border-radius: 2px 2px 0 0 ; + .bk-lesscode-tab-content { + display: none; } } - .bk-lesscode-tab-content { - display: none; - } } .header-operate-area { position: relative; @@ -79,4 +87,10 @@ color: #3a84ff; } } + .bk-lesscode-table { + border-bottom: 1px solid #dcdee5; + .bk-lesscode-table-body tbody tr td { + border-bottom-color: #dcdee5; + } + } } \ No newline at end of file diff --git a/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.js b/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.js new file mode 100644 index 000000000..e0887d8bb --- /dev/null +++ b/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.js @@ -0,0 +1,53 @@ +import { h } from 'bk-lesscode-render' +import LC from '@/element-materials/core' +import processOverview from '../../flow-manage-container/preview/components/process-overview' +import './index.postcss' + +export default { + name: 'widget-flow-workbench-container-edit', + inheritAttrs: false, + props: { + componentData: { + type: Object, + required: true + } + }, + data () { + return {} + }, + mounted () { + LC.addEventListener('update', this.updateCallBack) + }, + beforeDestroy () { + LC.removeEventListener('update', this.updateCallBack) + }, + methods: { + updateCallBack (event) { + if (event.target.componentId === this.componentData.componentId) { + this.$forceUpdate() + } + } + }, + render (render) { + h.init(render) + + return h({ + component: 'div', + class: ['flow-process-table'], + children: [ + h({ + component: 'div', + class: 'container-title', + children: ['我的流程任务'] + }), + h({ + component: processOverview, + props: { + ref: 'process', + isWorkbench: true + } + }) + ] + }) + } +} diff --git a/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.postcss b/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.postcss new file mode 100644 index 000000000..267c6df59 --- /dev/null +++ b/lib/client/src/components/render/pc/widget/flow-workbench-container/edit/index.postcss @@ -0,0 +1,31 @@ +.flow-process-table { + padding: 20px 24px; + background: #FFFFFF; + border: 1px dashed #CCCCCC; + .container-title { + margin-bottom: 12px; + font-size: 16px; + color: #313238; + } + .filter-render-wrapper { + padding: 16px 24px; + background: #FAFBFC; + } + + .bk-form-vertical, .bk-lesscode-form--vertical { + display: flex; + .bk-form-item, .bk-lesscode-form-item { + flex: 1; + margin-top: 0 !important; + } + } + .process-render * { + pointer-events: auto !important; + } + .bk-lesscode-table { + border-bottom: 1px solid #dcdee5; + .bk-lesscode-table-body tbody tr td { + border-bottom-color: #dcdee5; + } + } +} \ No newline at end of file diff --git a/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.js b/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.js new file mode 100644 index 000000000..d0ea3db69 --- /dev/null +++ b/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.js @@ -0,0 +1,38 @@ +import { h } from 'bk-lesscode-render' +import './index.postcss' +import processOverview from '../../flow-manage-container/preview/components/process-overview' + +export default { + name: 'widget-flow-workbench-container-preview', + props: { + componentData: { + type: Object, + required: true + } + }, + data () { + return {} + }, + render (render) { + h(render) + + return h({ + component: 'div', + class: ['widget-flow-workbench-container'], + children: [ + h({ + component: 'div', + class: 'container-title', + children: ['我的流程任务'] + }), + h({ + component: processOverview, + props: { + ref: 'process', + isWorkbench: true + } + }) + ] + }) + } +} diff --git a/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.postcss b/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.postcss new file mode 100644 index 000000000..e6d0b15e6 --- /dev/null +++ b/lib/client/src/components/render/pc/widget/flow-workbench-container/preview/index.postcss @@ -0,0 +1,15 @@ +.widget-flow-workbench-container { + padding: 20px 24px; + background: #ffffff; + .container-title { + margin-bottom: 12px; + font-size: 16px; + color: #313238; + } + .bk-lesscode-table { + border-bottom: 1px solid #dcdee5; + .bk-lesscode-table-body tbody tr td { + border-bottom-color: #dcdee5; + } + } +} \ No newline at end of file diff --git a/lib/client/src/components/render/pc/widget/table/table-column.js b/lib/client/src/components/render/pc/widget/table/table-column.js index a845f6f35..5499bde9e 100644 --- a/lib/client/src/components/render/pc/widget/table/table-column.js +++ b/lib/client/src/components/render/pc/widget/table/table-column.js @@ -219,7 +219,7 @@ export default { component: self.columnType, props: { formatter: self.bkTableFormatter, - renderHeader: self.renderHeader, + renderHeader: self.$props.type === 'selection' ? undefined : self.renderHeader, ...self.$props, type: self.$props.type }, diff --git a/lib/client/src/element-materials/core/static/create-node.js b/lib/client/src/element-materials/core/static/create-node.js index 25461cee4..74c637c99 100644 --- a/lib/client/src/element-materials/core/static/create-node.js +++ b/lib/client/src/element-materials/core/static/create-node.js @@ -3,8 +3,8 @@ import getMaterial from './get-material' import Node from '../Node' -export const createNode = (type, name) => { - const material = getMaterial(type, name) +export const createNode = (type, name, framework) => { + const material = getMaterial(type, name, framework) if (!material) { throw new Error(window.i18n.t('不支持的组件 * {0} *', [type])) } diff --git a/lib/client/src/element-materials/core/static/get-material.js b/lib/client/src/element-materials/core/static/get-material.js index f6bb3a507..c584d8737 100644 --- a/lib/client/src/element-materials/core/static/get-material.js +++ b/lib/client/src/element-materials/core/static/get-material.js @@ -56,13 +56,14 @@ export const unregisterMaterial = () => { }) } -export default function (type, name) { +export default function (type, name, customFramework) { /** bk-charts hack config settings * bk-charts存在同type,但是配置不同的情况 * - 在面板拖拽时,已经由hacker.js处理,因此有些地方可能不会传入name * - 在右侧props属性面板渲染时,会传入name,获取准确的配置 */ - const elementMaterials = framework === 'vue2' ? vue2ElementMaterials : vue3ElementMaterials + const materialFramework = customFramework || framework + const elementMaterials = materialFramework === 'vue2' ? vue2ElementMaterials : vue3ElementMaterials // 同一个type名称,对应不同组件,需要根据name进一步区分 const sameTypeArr = ['bk-charts', 'chart', 'echarts', 'bk-input', 'bk-radio-group', 'widget-van-picker'] @@ -80,7 +81,7 @@ export default function (type, name) { return _.cloneDeep(material) } - const material = materialMap[framework][type] + const material = materialMap[materialFramework][type] if (!material) { return null } diff --git a/lib/client/src/element-materials/materials/vue2/bk/data-manage-container/index.js b/lib/client/src/element-materials/materials/vue2/bk/data-manage-container/index.js index 89dfbf702..3d1ed99ba 100644 --- a/lib/client/src/element-materials/materials/vue2/bk/data-manage-container/index.js +++ b/lib/client/src/element-materials/materials/vue2/bk/data-manage-container/index.js @@ -12,6 +12,14 @@ export default { ], renderStyles: {}, props: { + flowTplId: { // 流程模板id + type: 'hidden', + val: '' + }, + nodeId: { // 流程节点的id + type: 'hidden', + val: '' + }, formId: { type: 'hidden', val: '' diff --git a/lib/client/src/element-materials/materials/vue2/bk/flow-workbench-container/index.js b/lib/client/src/element-materials/materials/vue2/bk/flow-workbench-container/index.js new file mode 100644 index 000000000..fbb08f6a2 --- /dev/null +++ b/lib/client/src/element-materials/materials/vue2/bk/flow-workbench-container/index.js @@ -0,0 +1,15 @@ +export default { + name: 'flow-workbench-container', + type: 'widget-flow-workbench-container', + displayName: '流程工作台容器', + icon: 'bk-drag-icon bk-drag-flow', + group: '容器', + order: 1, + events: [], + styles: [ + 'size', + 'margin' + ], + renderStyles: {}, + props: {} +} diff --git a/lib/client/src/element-materials/materials/vue2/bk/form-container/index.js b/lib/client/src/element-materials/materials/vue2/bk/form-container/index.js index 9e7e14811..0dd6f8740 100644 --- a/lib/client/src/element-materials/materials/vue2/bk/form-container/index.js +++ b/lib/client/src/element-materials/materials/vue2/bk/form-container/index.js @@ -15,6 +15,9 @@ export default { dataSource: { type: 'hidden', val: { + action: 'submitData', // 提交按钮的行为,submitData(存储数据)、executeFlow(流程提单) + flowTplId: '', // 流程模板id, action为executeFlow时需要 + nodeId: '', // 流程第一个节点为人工节点,该节点的id, action为executeFlow时需要 type: 'NEW_FORM', // NEW_FORM、USE_FORM relatedId: '', // 关联id,引用或复用的表单id id: '', // 表单id,若为复用类型则取所复用表单id @@ -32,8 +35,8 @@ export default { actions: { type: 'hidden', val: { - submit: false, - reset: false + submit: true, + reset: true } } } diff --git a/lib/client/src/element-materials/materials/vue2/bk/index.js b/lib/client/src/element-materials/materials/vue2/bk/index.js index b526e6a31..2f8f10a0d 100644 --- a/lib/client/src/element-materials/materials/vue2/bk/index.js +++ b/lib/client/src/element-materials/materials/vue2/bk/index.js @@ -16,6 +16,7 @@ import column from './column' import formContainer from './form-container' import dataManageContainer from './data-manage-container' import flowManageContainer from './flow-manage-container' +import flowWorkbenchContainer from './flow-workbench-container' import input from './input' import textarea from './input-textarea' @@ -101,6 +102,7 @@ const bkComponents = Object.seal([ formContainer, dataManageContainer, flowManageContainer, + flowWorkbenchContainer, mdEditor, paragraph, button, diff --git a/lib/client/src/element-materials/materials/vue3/bk/data-manage-container/index.js b/lib/client/src/element-materials/materials/vue3/bk/data-manage-container/index.js index 89dfbf702..3d1ed99ba 100644 --- a/lib/client/src/element-materials/materials/vue3/bk/data-manage-container/index.js +++ b/lib/client/src/element-materials/materials/vue3/bk/data-manage-container/index.js @@ -12,6 +12,14 @@ export default { ], renderStyles: {}, props: { + flowTplId: { // 流程模板id + type: 'hidden', + val: '' + }, + nodeId: { // 流程节点的id + type: 'hidden', + val: '' + }, formId: { type: 'hidden', val: '' diff --git a/lib/client/src/element-materials/materials/vue3/bk/flow-workbench-container/index.js b/lib/client/src/element-materials/materials/vue3/bk/flow-workbench-container/index.js new file mode 100644 index 000000000..fbb08f6a2 --- /dev/null +++ b/lib/client/src/element-materials/materials/vue3/bk/flow-workbench-container/index.js @@ -0,0 +1,15 @@ +export default { + name: 'flow-workbench-container', + type: 'widget-flow-workbench-container', + displayName: '流程工作台容器', + icon: 'bk-drag-icon bk-drag-flow', + group: '容器', + order: 1, + events: [], + styles: [ + 'size', + 'margin' + ], + renderStyles: {}, + props: {} +} diff --git a/lib/client/src/element-materials/materials/vue3/bk/form-container/index.js b/lib/client/src/element-materials/materials/vue3/bk/form-container/index.js index 9e7e14811..3275df9aa 100644 --- a/lib/client/src/element-materials/materials/vue3/bk/form-container/index.js +++ b/lib/client/src/element-materials/materials/vue3/bk/form-container/index.js @@ -15,6 +15,9 @@ export default { dataSource: { type: 'hidden', val: { + action: 'submitData', // 提交按钮的行为,submitData(存储数据)、executeFlow(流程提单) + flowTplId: '', // 流程模板id, action为executeFlow时需要 + nodeId: '', // 流程第一个节点为人工节点,该节点的id,action为executeFlow时需要 type: 'NEW_FORM', // NEW_FORM、USE_FORM relatedId: '', // 关联id,引用或复用的表单id id: '', // 表单id,若为复用类型则取所复用表单id @@ -32,8 +35,8 @@ export default { actions: { type: 'hidden', val: { - submit: false, - reset: false + submit: true, + reset: true } } } diff --git a/lib/client/src/element-materials/materials/vue3/bk/index.js b/lib/client/src/element-materials/materials/vue3/bk/index.js index 4e1a894f9..730212178 100644 --- a/lib/client/src/element-materials/materials/vue3/bk/index.js +++ b/lib/client/src/element-materials/materials/vue3/bk/index.js @@ -24,6 +24,7 @@ import collapse from './collapse' import colorPicker from './color-picker' import dataManageContainer from './data-manage-container' import flowManageContainer from './flow-manage-container' +import flowWorkbenchContainer from './flow-workbench-container' import datePicker from './date-picker' import dialog from './dialog' import divider from './divider' @@ -102,6 +103,7 @@ const bkComponents = Object.seal([ colorPicker, dataManageContainer, flowManageContainer, + flowWorkbenchContainer, datePicker, dialog, divider, diff --git a/lib/client/src/element-materials/modifier/component/data-manage-container/index.vue b/lib/client/src/element-materials/modifier/component/data-manage-container/index.vue index b1c6171b9..52a7828b7 100644 --- a/lib/client/src/element-materials/modifier/component/data-manage-container/index.vue +++ b/lib/client/src/element-materials/modifier/component/data-manage-container/index.vue @@ -96,6 +96,29 @@ this.loading = false }, handleDataManageSelectForm (val) { + const regex = /^flow-tpl-(?\d+)-(?[a-z0-9]+)$/; + const form = this.formList.find(formItem => formItem.id === val) + if (form) { + const match = form.componentId.match(regex); + if (match && match.groups.tplId && match.groups.nodeId) { + const tplId = Number(match.groups.tplId) + this.activeNode.setProp('flowTplId', { + ...this.activeNode.renderProps.flowTplId, + code: tplId, + renderValue: tplId + }) + this.activeNode.setProp('nodeId', { + ...this.activeNode.renderProps.nodeId, + code: match.groups.nodeId, + renderValue: match.groups.nodeId + }) + } + } + this.activeNode.setProp('tableName', { + ...this.activeNode.renderProps.tableName, + code: form.tableName, + renderValue: form.tableName + }) this.activeNode.setProp('formId', { ...this.activeNode.renderProps.formId, code: val, diff --git a/lib/client/src/element-materials/modifier/component/form-container/components/data-source.vue b/lib/client/src/element-materials/modifier/component/form-container/components/data-source.vue index e49d8bbb3..b5eadbc62 100644 --- a/lib/client/src/element-materials/modifier/component/form-container/components/data-source.vue +++ b/lib/client/src/element-materials/modifier/component/form-container/components/data-source.vue @@ -1,27 +1,59 @@ + diff --git a/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue b/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue index c66db25df..e57726fd0 100644 --- a/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue +++ b/lib/client/src/views/project/flow-manage/tpl-edit/config/index.vue @@ -19,6 +19,14 @@ :placeholder="$t('请输入流程模板名称')" :show-word-limit="true" /> + + +

@@ -53,12 +61,16 @@