diff --git a/packages/designer/src/components/composition/command/supported-controllers/pc-supported-controller.json b/packages/designer/src/components/composition/command/supported-controllers/pc-supported-controller.json index 7ec241d2ae59a08a33d2b2653156b5d65677d45b..993fa01ab4e47ff8582a39b5119586b5517157af 100644 --- a/packages/designer/src/components/composition/command/supported-controllers/pc-supported-controller.json +++ b/packages/designer/src/components/composition/command/supported-controllers/pc-supported-controller.json @@ -584,5 +584,27 @@ "id": "b707e9c2-61c7-01d8-bd0e-aac222271602", "code": "PreviewBySubDirName" } + ], + "76a6ee73-c068-4834-be8c-ef14e80fe325":[ + { + "id": "97d236be-4ad0-7421-f9f0-43e91a6e740a", + "code": "queryComments" + }, + { + "id": "0a815173-7bcf-48ca-15a7-8f945b088ed0", + "code": "queryAtUsers" + }, + { + "id": "7bb99c69-49a0-9e17-bebe-e92279f3edd0", + "code": "addComment" + }, + { + "id": "28e1ee60-e08e-2a27-8a46-d647b0d33e29", + "code": "queryAllOrgs" + }, + { + "id": "5398e7f7-d938-1536-06c8-58cc94fe64c6", + "code": "queryFrequentAtUsers" + } ] } \ No newline at end of file diff --git a/packages/designer/src/components/types/toolbox/pc-toolbox.json b/packages/designer/src/components/types/toolbox/pc-toolbox.json index fc4216ac4e9ee8aa4a3956fd4aceefcfdf409565..59c606d37411c9fa9363f94677a00f4c61a876f3 100644 --- a/packages/designer/src/components/types/toolbox/pc-toolbox.json +++ b/packages/designer/src/components/types/toolbox/pc-toolbox.json @@ -16,7 +16,7 @@ "category": "input", "hideInControlBox": false }, - { + { "id": "RichTextEditor", "type": "rich-text-editor", "name": "富文本", @@ -83,6 +83,13 @@ "type": "time-picker", "name": "时间选择", "category": "input" + }, + { + "id": "Image", + "type": "image", + "name": "图像", + "category": "input", + "icon": "image" } ] }, @@ -174,6 +181,20 @@ "type": "filter-bar", "name": "筛选条", "category": "container" + }, + { + "id": "DiscussionEditor", + "type": "discussion-editor", + "name": "评论编辑区", + "category": "discussion", + "icon": "discussion-editor" + }, + { + "id": "DiscussionList", + "type": "discussion-list", + "name": "评论列表", + "category": "discussion", + "icon": "discussion-list" } ] }, diff --git a/packages/ui-vue/components/components.ts b/packages/ui-vue/components/components.ts index dcf0405b6e19e0d993b7e240714053dbe498db5a..63cab1d36cf030f1a571c199d4d966b1b546b49f 100644 --- a/packages/ui-vue/components/components.ts +++ b/packages/ui-vue/components/components.ts @@ -114,5 +114,11 @@ export { default as FPropertyEditor } from './property-editor'; export { default as MenuLookupContainer } from './menu-lookup/src/components/modal-container.component'; export { useMenuTreeGridCoordinator } from './menu-lookup/src/composition/use-tree-grid-coordinator'; export { default as FLookup } from './lookup'; +export { default as FImage } from './image'; +export type { ImageProps } from './image'; +export { default as FDiscussionEditor } from './discussion-editor'; +export type { DiscussionEditorProps } from './discussion-editor'; +export { default as FDiscussionList } from './discussion-list'; +export type { DiscussionListProps } from './discussion-list'; export { default as Locale, LocaleService, useResourceLoader, LOCALE_SERVICE_INJECTION_TOKEN, type LocaleConfig } from './locale'; -export * from './common'; \ No newline at end of file +export * from './common'; diff --git a/packages/ui-vue/components/designer-canvas/src/components/maps.ts b/packages/ui-vue/components/designer-canvas/src/components/maps.ts index 20e5683b27f2ccb27ca1b4aec634cda612c3f5e2..f29e1b69cc10dd5f49cabd7af9d5157bebca1023 100644 --- a/packages/ui-vue/components/designer-canvas/src/components/maps.ts +++ b/packages/ui-vue/components/designer-canvas/src/components/maps.ts @@ -57,6 +57,9 @@ import FTreeGrid from '@farris/ui-vue/components/tree-grid/designer'; import FFieldset from '@farris/ui-vue/components/fieldset'; import FDrawer from '@farris/ui-vue/components/drawer/designer'; import FHtmlTemplate from '@farris/ui-vue/components/html-template'; +import FImage from '@farris/ui-vue/components/image'; +import FDiscussionEditor from '@farris/ui-vue/components/discussion-editor'; +import FDiscussionList from '@farris/ui-vue/components/discussion-list'; import { RegisterContext, useThirdComponent } from '@farris/ui-vue/components/common'; import { createPropsResolver, propertyConfigSchemaMapForDesigner, propertyEffectMapForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; import { schemaMapForDesigner, schemaResolverMapForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; @@ -134,6 +137,10 @@ function loadDesignerRegister() { FTreeGrid.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FFieldset.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FDrawer.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FHtmlTemplate.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FImage.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FDiscussionEditor.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FDiscussionList.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FHtmlTemplate.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); // FHtmlEditor.createPropsResolver = createPropsResolver; @@ -167,6 +174,7 @@ function registerDesignerComponents(components: any[]) { components.forEach(component => { component.registerDesigner && component.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter, registerContext); }); + } diff --git a/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts b/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts index 3afbc8f6fc8303e936b1361aa9118020d848bf20..afb4f009b8a0dcff444f37975fa99d85132998a7 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts @@ -86,10 +86,13 @@ export const DgControl = { 'list-nav': { type: 'list-nav', name: '列表导航' }, 'list-view': { type: 'list-view', name: '列表' }, - + 'filter-bar': { type: 'filter-bar', name: '筛选条' }, 'language-textbox': { type: 'language-textbox', name: '多语输入框' }, + 'image': { type: 'image', name: '图像' }, + 'discussion-editor': { type: 'discussion-editor', name: '评论编辑区' }, + 'discussion-list': { type: 'discussion-list', name: '评论列表' }, 'rich-text-editor': { type: 'rich-text-editor', name: '富文本编辑器' }, }; diff --git a/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx b/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx deleted file mode 100644 index b1af9644d9296f0307091d81b044380356a7cd31..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx +++ /dev/null @@ -1,552 +0,0 @@ - -/* eslint-disable no-use-before-define */ -/** - * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { computed, defineComponent, onMounted, ref, SetupContext } from 'vue'; -import { discussionEditorProps, DiscussionEditorProps } from './discussion-editor.props'; -import { MsgInfo, editAttachFile } from './src/types/interface'; -import FInputGroup from '@farris/ui-vue/components/input-group'; -import { useDiscussionEditor } from './src/composition/use-discussion-editor'; -import './discussion-editor.scss'; -import { LocaleService } from '../locale'; - -export default defineComponent({ - name: 'FDiscussionEditor', - props: discussionEditorProps, - emits: ['selections', 'lineData', 'value', 'filePreview', 'fileRemove', 'fileUploadDone', 'personnelSearch', 'getOutUsers'] as (string[] & ThisType) | undefined, - setup(props: DiscussionEditorProps, context: SetupContext) { - const cancelVisible = ref(props.cancelVisible); - const personnelsPrimaryKey = ref(props.personnelsPrimaryKey); - const replyPersonnelsDisplayKey = ref(props.replyPersonnelsDisplayKey); - const editHeight = ref(props.editHeight); - const type = ref(props.type); - const orgUrl = ref(props.orgUrl); - const sectionData = ref(props.sectionData); - - let options: any; - let placeholder: string; - /** 暂存文本框的输入,解决焦点问题 */ - let tempTextValue: string; - - /** 人事弹窗列表数据 */ - const _personnels = ref(props.personnels); - - const personnels = computed({ - get() { - return _personnels.value; - }, - set(val) { - if (val) { - _personnels.value = val; - innerPersonnels = _personnels.value; - copyPersonnels = _personnels.value; - } - } - }); - - /** 人事弹窗列表数据 */ - const _replyUser = ref(props.replyUser); - - const replyUser = computed({ - get() { - return _replyUser.value; - }, - set(val: any) { - if (val) { - _replyUser.value = val; - if (_replyUser.value.id) { - editorFocus(); - } - } - } - }); - - const _attachFiles = ref(props.attachFiles); - - const attachFiles = computed({ - get() { - return _attachFiles.value; - }, - set(val: any) { - if (val) { - _attachFiles.value = val; - } - } - }); - - /** 审批意见 */ - const textValue = ref(''); - - /** 暂存人员信息 */ - const tempPersonnelsValue = ref(''); - /** 暂存部门 */ - const tempSectionValue = ref(''); - - /** 选择要发送的部门 */ - const selectedSection: any = []; - /** 搜索文本框中绑定的值 */ - let personnelText: any; - /** 暂存人员信息,用于搜索 */ - let copyPersonnels: any = []; - let innerPersonnels: any = []; - // let el: ElementRef; - /** 避免重复输入的token */ - let token: boolean; - let permission: any; - /** 上传附件是否显示 */ - const attachFilesModalVisible = ref(false); - const searchPersonnelList: any = {}; - const showSearchList = ref(false); - const permissionList = ref(); - const groupIcon = ''; - - const { personSearchUrl, personnelsDisplayKey, personModalVisible, relativeVisible, selectedPersonnels, - stopBubble, _isInArray, getSearchData, getAvatar, setRelativeValue } = - useDiscussionEditor(props, context); - onMounted(() => { - document.addEventListener('click', setRelativeValue); - permissionList.value = [ - { value: 'ALL', text: LocaleService.getLocaleValue('discussionGroup.all') }, - { value: 'RELATED', text: LocaleService.getLocaleValue('discussionGroup.related') } - ]; - permission = permissionList.value[0]; - options = { maxUploads: 3, maxFileSize: 10240, allowedContentTypes: ['.jpg', '.pdf'] }; - placeholder = LocaleService.getLocaleValue('discussionGroup.placeholder') as string; - } - ); - - /** 文本框失去焦点触发 */ - function setTextValue(e: any) { - if (e) { - tempTextValue = e.target.innerHTML; - textValue.value = tempTextValue; - } - if (tempPersonnelsValue.value) { - textValue.value += tempPersonnelsValue.value; - } - if (tempSectionValue.value) { - textValue.value += tempSectionValue.value; - } - tempTextValue = ''; - tempPersonnelsValue.value = ''; - tempSectionValue.value = ''; - } - /** 监听键盘事件, 主要是用于删除@人 */ - function listenEditorValueChange(e: any) { - tempTextValue = e.target.innerHTML; - const { children } = e.target; - const childrenId: any = []; - for (let i = 0; i < children.length; i++) { - childrenId.push(children[i].id); - } - selectedPersonnels.value.forEach((personnel: any, index: any) => { - if (!childrenId.includes(personnel[personnelsPrimaryKey.value])) { - selectedPersonnels.value.splice(index, 1); - } - }); - selectedSection.value.forEach((section: any, index: any) => { - if (!childrenId.includes(section[personnelsPrimaryKey.value])) { - selectedSection.value.splice(index, 1); - } - }); - if (!tempTextValue) { - tempTextValue = ''; - } - } - /** - * 搜索人员 - */ - function searchPersonnel() { - - if (personnelText) { - showSearchList.value = true; - getSearchData(personnelText, 0).then((d: any) => { - if ("users" in d) { - searchPersonnelList.value = d; - setPersonModalPosition(); - } - }); - } - else { - showSearchList.value = false; - } - - } - /** - * 搜索下一页 - */ - function getMoreSearchData() { - getSearchData(personnelText, searchPersonnelList.pageIndex + 1).then((d: any) => { - if ("users" in d) { - searchPersonnelList.pageIndex = d.pageIndex; - searchPersonnelList.users = [...searchPersonnelList.users, ...d.users]; - } - }); - } - - /** 增加 @ 人员 */ - function appendPersonnels() { - let selectedList = []; - if (!showSearchList.value) { - selectedList = innerPersonnels.filter((item: any) => { item.active === true; }); - } - else { - selectedList = searchPersonnelList.users.filter((item: any) => item.active === true); - } - if (selectedList.length) { - appendPersonnel(selectedList); - } - resetPersonnels(); - setTextValue(null); - personModalVisible.value = false; - } - /** 高级搜索人员添加 */ - function appendPersonnelsList(listData: any) { - if (listData.length) { - appendPersonnel(listData, true); - } - setTextValue(null); - } - /** 循环增加人员 */ - function appendPersonnel(listData: any, external = false) { - listData.forEach((item: any) => { - if (!(selectedPersonnels.value.length && _isInArray(item[personnelsPrimaryKey.value], personnelsPrimaryKey.value, selectedPersonnels))) { - tempPersonnelsValue.value += '@' + item[personnelsDisplayKey.value] + ' '; - selectedPersonnels.value.push(item); - } - }); - } - /** - * 添加部门 - * @param listData - */ - function appendSectionList(listData: any) { - if (listData.length) { - appendSection(listData); - } - setTextValue(null); - } - - function appendSection(listData: any) { - listData.forEach((item: any) => { - if (!(selectedSection.value.length && _isInArray(item[personnelsPrimaryKey.value], personnelsPrimaryKey.value, selectedSection))) { - tempSectionValue.value += '@' + item.name + ' '; - selectedSection.value.push(item); - } - }); - } - - /** - * 关闭人事管理弹窗 - */ - function resetPersonnels() { - showSearchList.value = false; - personModalVisible.value = false; - innerPersonnels = copyPersonnels; - if (innerPersonnels.length) { - innerPersonnels.forEach((item: any) => { item.active = false; }); - } - personnelText = ''; - } - - /** - * 打开人员管理 - * @param e 事件 - */ - function openModalPerson(e: any) { - personModalVisible.value = !personModalVisible.value; - setTimeout(() => { - if (personModalVisible.value) { - setPersonModalPosition(); - } - }, 0); - stopBubble(e); - } - const personModal = ref(null); - function setPersonModalPosition() { - const winH = window.innerHeight; - const ModalBottom = personModal.value?.getBoundingClientRect().bottom; - if (ModalBottom && winH < ModalBottom) { - personModal.value?.scrollIntoView(false); - } - } - - /** - * 提交评语 - */ - function submitApproval() { - if (!textValue.value) { - const notEmptyText = LocaleService.getLocaleValue('discussionGroup.notEmpty'); - if (notEmptyText) { - // notifyService.error(notEmptyText); - } - return; - } - const editAttachFiles: editAttachFile[] = []; - if (attachFiles.value && attachFiles.value.length) { - attachFiles.value.forEach((file: any) => { - const { id } = file; - const { name } = file; - const { size } = file; - const { metadataId } = file.extend; - const attachFile = { - id, - name, - size, - metadataId - }; - editAttachFiles.push(attachFile); - }); - } - context.emit('value', { - msgInfo: MsgInfo.Confirm, - text: textValue, - mailTos: selectedPersonnels, - mailToSections: selectedSection, - visibility: permission.value, - parentId: (replyUser.value && 'id' in replyUser.value) ? replyUser.value.id : null, - attachFiles: editAttachFiles.length ? editAttachFiles : null - }); - textValue.value = ''; - selectedPersonnels.value = []; - selectedSection.value = []; - attachFiles.value = []; - replyUser.value = {}; - } - - function cancel() { - context.emit('value', { - msgInfo: MsgInfo.Cancel, - text: null, - mailTos: [], - mailToSections: [], - visibility: null, - parentId: null, - attachFiles: null - }); - textValue.value = ''; - selectedPersonnels.value = []; - selectedSection.value = []; - attachFiles.value = []; - replyUser.value = {}; - } - - const editor = ref(null); - /** 获得焦点 */ - function editorFocus() { - editor.value?.focus(); - } - /** - * 高级人员点确认 - */ - function selectionsChangePar(event: any) { - if (event.data.users.length) { - const userList: any = []; - event.data.users.forEach((user: any) => { - userList.push(user.data); - }); - appendPersonnelsList(userList); - } - if (event.data.section.length) { - const sectionList: any = []; - event.data.section.forEach((sec: any) => { - sectionList.push(sec.data); - }); - appendSectionList(sectionList); - } - context.emit('selections', event); - } - /** 高级人员中选中某行 */ - function lineDataChangePar(event: any) { - context.emit('lineData', event); - } - - function outUsers(event: any) { - context.emit('getOutUsers', event); - } - - function activeStateChanged(item: any) { - item.active = !item.active; - return item; - }; - - return () => { - return ( -
- {(replyUser.value && replyUser.value.id) && ( -
- {'discussionGroup.reply'} - - {replyUser.value[replyPersonnelsDisplayKey.value]} - - : -
- )} -
-
-
listenEditorValueChange(e)} - onBlur={(e) => setTextValue(e)} - ref={editor} - contenteditable={true} - innerHTML={textValue.value} - >
-
- -
-
- ); - }; - } -}); diff --git a/packages/ui-vue/components/discussion-editor/index.ts b/packages/ui-vue/components/discussion-editor/index.ts index aa06870b31d172610dccbbbf8496336054b9c11c..c1c3fdab92fde5a1ac61ea9fb8f33ada31bed111 100644 --- a/packages/ui-vue/components/discussion-editor/index.ts +++ b/packages/ui-vue/components/discussion-editor/index.ts @@ -14,14 +14,25 @@ * limitations under the License. */ import type { App } from 'vue'; -import DiscussionEditorProps from './discussion-editor.component'; +import FDiscussionEditor from './src/discussion-editor.component'; +import { propsResolver } from './src/discussion-editor.props' +import FDiscussionEditorDesign from './src/designer/discussion-editor.design.component'; +import { propsDesignResolver } from './src/designer/discussion-editor.design.props' +import { withInstall } from '@farris/ui-vue/components/common'; -export * from './discussion-editor.props'; +export * from './src/discussion-editor.props'; -export { DiscussionEditorProps }; - -export default { - install(app: App): void { - app.component(DiscussionEditorProps.name as string, DiscussionEditorProps); - } +FDiscussionEditor.register = ( + componentMap: Record, propsResolverMap: Record, + configResolverMap: Record, resolverMap: Record): void => { + componentMap['discussion-editor'] = FDiscussionEditor; + propsResolverMap['discussion-editor'] = propsResolver; +}; +FDiscussionEditor.registerDesigner = (componentMap: Record, propsResolverMap: Record, + configResolverMap: Record): void => { + componentMap['discussion-editor'] = FDiscussionEditorDesign; + propsResolverMap['discussion-editor'] = propsDesignResolver; }; +export { FDiscussionEditor }; + +export default withInstall(FDiscussionEditor); \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-editor/src/components/colleague-panel.component.tsx b/packages/ui-vue/components/discussion-editor/src/components/colleague-panel.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7d437301bc96a5d1717ae162cbbbd12d3c359b01 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/components/colleague-panel.component.tsx @@ -0,0 +1,242 @@ +import { SetupContext, computed, defineComponent, ref, watch } from "vue"; +import FPopover from '@farris/ui-vue/components/popover'; +import FInputGroup from '@farris/ui-vue/components/input-group'; +import { useDiscussionEditor } from "../composition/use-discussion-editor"; +import { UsePopup } from "../types/composition"; +import { DiscussionEditorProps, discussionEditorProps } from "../discussion-editor.props"; +import { cloneDeep } from "lodash"; + + +export default defineComponent({ + name: 'FColleaguePanel', + props: discussionEditorProps, + emits: ['submit', 'cancel'] as (string[] & ThisType) | undefined, + setup(props: DiscussionEditorProps, context: SetupContext) { + const popoverRef = ref(); + const popoverInstance = computed(() => popoverRef); + + const { _isInArray, personnelsDisplayKey, stopBubble, getSearchData, getAvatar, ...otherEditorInfo } = useDiscussionEditor(props, context); + // 搜索框提示文本 + const placeholder = '请输入姓名搜索'; + // 搜索框提示图标 + const groupIcon = ''; + /** 保存查询人员返回的结果 */ + let searchPersonnelList: any = {}; + /** 搜索文本框中绑定的值 */ + const searchText: any = ref(''); + /** 当@ 人员更新的时候,刷新 */ + const personnelsUpdate = ref(0); + /** 避免重复输入的token */ + let token: boolean; + /** 暂存人员信息 */ + const tempPersonnelsValue = ref(''); + /** 暂存文本框的输入,解决焦点问题 tempTextValue 移除*/ + /** 列表绑定数据 */ + let listBindingDatas: any = props.personnels.length ? cloneDeep(props.personnels) : []; + /** 列表实例 */ + const listviewRef = ref(); + + // 评论信息 + const textValue = ref(''); + + const personnelsPrimaryKey = ref(props.personnelsPrimaryKey); + // 分页信息 + const defaultPagination = { + total: props.personnels.length, + enable: false, + mode: 'server', + index: 1, + size: 20, + showPageInfo: false, + showLimits: false + }; + const paginationInfo = ref(defaultPagination); + + /** + * 更新绑定人员列表的数据 + */ + function updateListBindingData() { + if (searchText.value) { + listBindingDatas = searchPersonnelList.users; + + } else { + // 更新为旧数据 + listBindingDatas = props.personnels.length ? cloneDeep(props.personnels) : []; + } + personnelsUpdate.value++; + // listviewRef?.value.updateDataSource(listBindingDatas); + } + /** + * 常用@ 人员更新 + */ + watch(() => props.personnels, () => { + updateListBindingData(); + // personnelsUpdate.value++; + }); + /** + * 搜索人员 + */ + function searchPersonnel() { + if (searchText.value) { + getSearchData(searchText.value, 0).then((d: any) => { + if ("users" in d) { + searchPersonnelList = d; + paginationInfo.value = Object.assign({}, defaultPagination, { enable: searchPersonnelList.pageCount > 1, total: d.pageCount }); + updateListBindingData(); + } + }); + } else { + updateListBindingData(); + } + } + + /** + * 搜索下一页 + */ + function pageIndexChanged(pageInfo) { + // 应该通过分页信息获取 + getSearchData(searchText.value, pageInfo.pageIndex).then((d: any) => { + if ("users" in d) { + searchPersonnelList.pageIndex = d.pageIndex; + searchPersonnelList.users = d.users; + } + }); + } + /** 循环增加人员 */ + function appendPersonnel(listData: any, external = false) { + listData.forEach((item: any) => { + if (!(otherEditorInfo.selectedPersonnels.length && _isInArray(item[personnelsPrimaryKey.value], personnelsPrimaryKey.value, otherEditorInfo.selectedPersonnels))) { + tempPersonnelsValue.value += '@' + item[personnelsDisplayKey.value] + ' '; + otherEditorInfo.selectedPersonnels.push(item); + } + }); + } + function closePersonnelsPanel() { + document.body.click(); + } + /** + * 关闭人事管理弹窗 + */ + function resetPersonnels() { + searchText.value = ''; + // 清空已选项 + listviewRef.value.clearSelection(); + // 更新绑定数据 + updateListBindingData(); + // 关闭下拉面板 + closePersonnelsPanel(); + } + /** 文本框失去焦点触发 */ + function setTextValue(e: any) { + if (e) { + textValue.value = e.target.innerHTML; + } + if (tempPersonnelsValue.value) { + textValue.value += tempPersonnelsValue.value; + } + tempPersonnelsValue.value = ''; + } + + /** 增加 @ 人员 */ + function appendPersonnels() { + // 获取列表选中信息 + const selectedList = listviewRef.value.getSelections(); + if (selectedList.length) { + appendPersonnel(selectedList); + } + resetPersonnels(); + setTextValue(null); + closePersonnelsPanel(); + context.emit('submit', textValue.value); + } + + /** 监听键盘事件, 主要是用于删除@人 */ + function keyUpChangeValueHandler(e: any) { + const { children } = e.target; + const childrenId: any = []; + for (let i = 0; i < children.length; i++) { + childrenId.push(children[i].id); + } + otherEditorInfo.selectedPersonnels.forEach((personnel: any, index: any) => { + if (!childrenId.includes(personnel[personnelsPrimaryKey.value])) { + otherEditorInfo.selectedPersonnels.splice(index, 1); + } + }); + } + + context.expose({ keyUpChangeValueHandler }); + // listview显示分页条--TODO + return () => { + return
+
+ @ + + 同事 + +
+ +
{ stopBubble(e); }}> + +
+
+ pageIndexChanged(info)} + > + {{ + content: (itemInfo) => { + return
+ {itemInfo.item.imgData && ( + + )} + {!itemInfo.item.imgData && ( +
+ )} +
+
{itemInfo.item[personnelsDisplayKey.value]}
+
{itemInfo.item.email}
+
+
; + } + }} +
+
+
+
+
+ + +
+
+
+
+
; + }; + } +}); diff --git a/packages/ui-vue/components/discussion-editor/src/components/personnel-management.component.tsx b/packages/ui-vue/components/discussion-editor/src/components/personnel-management.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..701f3939fba0a67e59927c87e0e3053a63a45c84 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/components/personnel-management.component.tsx @@ -0,0 +1,345 @@ +import { SetupContext, computed, defineComponent, ref, watch } from "vue"; +import FPopover from '@farris/ui-vue/components/popover'; +import FInputGroup from '@farris/ui-vue/components/input-group'; +import { useDiscussionEditor } from "../composition/use-discussion-editor"; +import { UsePopup } from "../types/composition"; +import { DiscussionEditorProps, discussionEditorProps } from "../discussion-editor.props"; +import { cloneDeep } from "lodash"; + +/** + * 人员管理弹出窗口 ToDo + */ +export default defineComponent({ + name: 'FPersonnelManagement', + props: discussionEditorProps, + emits: ['submit', 'cancel'] as (string[] & ThisType) | undefined, + setup(props: DiscussionEditorProps, context: SetupContext) { + const popoverRef = ref(); + const popoverInstance = computed(() => popoverRef); + + const { _isInArray, personnelsDisplayKey, stopBubble, getSearchData, getAvatar, ...otherEditorInfo} = useDiscussionEditor(props, context); + // 搜索框提示文本 + const placeholder = '请输入姓名搜索'; + // 搜索框提示图标 + const groupIcon = ''; + + const showSearchList = ref(false); + const searchPersonnelList: any = {}; + /** 搜索文本框中绑定的值 */ + const personnelText: any = ref(''); + /** 暂存人员信息,用于搜索 */ + let copyPersonnels: any = []; + /** 保存@ 人员的信息 */ + let innerPersonnels: any = props.personnels; + /** 当@ 人员更新的时候,刷新 */ + const personnelsUpdate = ref(0); + + // let el: ElementRef; + /** 避免重复输入的token */ + let token: boolean; + + /** 暂存人员信息 */ + const tempPersonnelsValue = ref(''); + /** 暂存部门 */ + const tempSectionValue = ref(''); + + /** 选择要发送的部门 */ + const selectedSection: any = []; + + /** 暂存文本框的输入,解决焦点问题 */ + let tempTextValue: string; + + // 评论信息 + const textValue = ref(''); + + const popoverVisible = ref(false); + + const personnelsPrimaryKey = ref(props.personnelsPrimaryKey); + + function updatePersonnels() { + copyPersonnels = props.personnels.length ? cloneDeep(props.personnels) : []; + innerPersonnels = props.personnels; + } + watch(() => props.personnels, () => { + updatePersonnels(); + personnelsUpdate.value++; + }); + updatePersonnels(); + /** + * 搜索人员 + */ + function searchPersonnel() { + + if (personnelText.value) { + showSearchList.value = true; + getSearchData(personnelText.value, 0).then((d: any) => { + if ("users" in d) { + searchPersonnelList.value = d; + // setPersonModalPosition(); + } + }); + } + else { + showSearchList.value = false; + } + + } + /** + * 搜索下一页 + */ + function getMoreSearchData() { + getSearchData(personnelText.value, searchPersonnelList.pageIndex + 1).then((d: any) => { + if ("users" in d) { + searchPersonnelList.pageIndex = d.pageIndex; + searchPersonnelList.users = [...searchPersonnelList.users, ...d.users]; + } + }); + } + /** 循环增加人员 */ + function appendPersonnel(listData: any, external = false) { + listData.forEach((item: any) => { + if (!(otherEditorInfo.selectedPersonnels.length && _isInArray(item[personnelsPrimaryKey.value], personnelsPrimaryKey.value, otherEditorInfo.selectedPersonnels))) { + tempPersonnelsValue.value += '@' + item[personnelsDisplayKey.value] + ' '; + otherEditorInfo.selectedPersonnels.push(item); + } + }); + } + function closePersonnelsPanel() { + document.body.click(); + } + /** + * 关闭人事管理弹窗 + */ + function resetPersonnels() { + showSearchList.value = false; + innerPersonnels = copyPersonnels; + if (innerPersonnels.length) { + innerPersonnels.forEach((item: any) => { item.active = false; }); + } + personnelText.value = ''; + closePersonnelsPanel(); + } /** 文本框失去焦点触发 */ + function setTextValue(e: any) { + if (e) { + tempTextValue = e.target.innerHTML; + textValue.value = tempTextValue; + } + if (tempPersonnelsValue.value) { + textValue.value += tempPersonnelsValue.value; + } + if (tempSectionValue.value) { + textValue.value += tempSectionValue.value; + } + tempTextValue = ''; + tempPersonnelsValue.value = ''; + tempSectionValue.value = ''; + } + + /** 增加 @ 人员 */ + function appendPersonnels() { + let selectedList = []; + if (!showSearchList.value) { + selectedList = innerPersonnels.filter((item: any) => { + return item.active === true; + }); + } + else { + selectedList = searchPersonnelList.users.filter((item: any) => item.active === true); + } + if (selectedList.length) { + appendPersonnel(selectedList); + } + resetPersonnels(); + setTextValue(null); + closePersonnelsPanel(); + context.emit('submit', textValue.value); + } + function activeStateChanged(item: any) { + item.active = !item.active; + return item; + }; + /** 高级搜索人员添加 */ + function appendPersonnelsList(listData: any) { + if (listData.length) { + appendPersonnel(listData, true); + } + setTextValue(null); + } + + function appendSection(listData: any) { + listData.forEach((item: any) => { + if (!(selectedSection.length && _isInArray(item[personnelsPrimaryKey.value], personnelsPrimaryKey.value, selectedSection))) { + tempSectionValue.value += '@' + item.name + ' '; + selectedSection.push(item); + } + }); + } + /** + * 添加部门 + * @param listData + */ + function appendSectionList(listData: any) { + if (listData.length) { + appendSection(listData); + } + setTextValue(null); + } + /** + * 高级人员点确认 + */ + function selectionsChangePar(event: any) { + if (event.data.users.length) { + const userList: any = []; + event.data.users.forEach((user: any) => { + userList.push(user.data); + }); + appendPersonnelsList(userList); + } + if (event.data.section.length) { + const sectionList: any = []; + event.data.section.forEach((sec: any) => { + sectionList.push(sec.data); + }); + appendSectionList(sectionList); + } + context.emit('selections', event); + } + /** 高级人员中选中某行 */ + function lineDataChangePar(event: any) { + context.emit('lineData', event); + } + + function outUsers(event: any) { + context.emit('getOutUsers', event); + } + + /** 监听键盘事件, 主要是用于删除@人 */ + function keyUpChangeValueHandler(e: any) { + tempTextValue = e.target.innerHTML; + const { children } = e.target; + const childrenId: any = []; + for (let i = 0; i < children.length; i++) { + childrenId.push(children[i].id); + } + otherEditorInfo.selectedPersonnels.forEach((personnel: any, index: any) => { + if (!childrenId.includes(personnel[personnelsPrimaryKey.value])) { + otherEditorInfo.selectedPersonnels.splice(index, 1); + } + }); + selectedSection.forEach((section: any, index: any) => { + if (!childrenId.includes(section[personnelsPrimaryKey.value])) { + selectedSection.splice(index, 1); + } + }); + if (!tempTextValue) { + tempTextValue = ''; + } + } + + context.expose({keyUpChangeValueHandler}); + + return () => { + return
+
+ @ + + 同事 + +
+ +
{ stopBubble(e); }}> + +
+ + +
+
+
+ + +
+
+
+
+
; + }; + } +}); diff --git a/packages/ui-vue/components/discussion-editor/src/composition/build-discussion-editor.ts b/packages/ui-vue/components/discussion-editor/src/composition/build-discussion-editor.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4ee9181723880a3b58a5e9ab6f975b826be8ac7 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/composition/build-discussion-editor.ts @@ -0,0 +1,170 @@ +import { getSchemaByType } from "@farris/ui-vue/components/dynamic-resolver"; +import { DesignerHostService, DgControl } from "@farris/ui-vue/components/designer-canvas"; +import { useGuid } from "@farris/ui-vue/components/common"; + +/** + * 创建筛选条 + */ +export function buildDiscussionEditor(context: Record, designerHostService?: DesignerHostService) { + const formSchemaUtils = designerHostService?.formSchemaUtils; + const targetComponentInstance = context.parentComponentInstance; + /** 评论区命令控制器(ListController)id */ + const discussionGroupControllerWebCmdId = '76a6ee73-c068-4834-be8c-ef14e80fe325'; + + /** + * 组装筛选条容器和筛选条元数据,并插入组件schema + */ + function createDiscussionEditorContainer(): { discussionEditor: any, discussionEditorContainer: any } | any { + const discussionEditorContainer = getSchemaByType(DgControl['content-container'].type); + const discussionEditorMetadata = getSchemaByType(DgControl['discussion-editor'].type); + if (!discussionEditorContainer || !discussionEditorMetadata) { + return; + } + const suffixId = Math.random().toString(36).substr(2, 4); + Object.assign(discussionEditorContainer, { + id: 'discussion-editor-container-' + suffixId, + // appearance: { + // class: '' + // }, + contents: [discussionEditorMetadata] + }); + + // 写预置的属性 + Object.assign(discussionEditorMetadata, { + id: 'discussion-editor-' + suffixId + }); + + return { discussionEditor: discussionEditorMetadata, discussionEditorContainer }; + } + + function createSingleDiscussionGroupCommand(viewModelInfo, commandInfo) { + let newDiscussionCommandId; + // 1、在组件中预置命令 + if (viewModelInfo && viewModelInfo.commands) { + let discussionGroupCommand = viewModelInfo.commands.find(command => command.handlerName === commandInfo['handlerName'] && command.cmpId === discussionGroupControllerWebCmdId); + if (discussionGroupCommand) { + if (discussionGroupCommand.params && discussionGroupCommand.params.length) { + // 重置参数 + discussionGroupCommand.params.forEach((item) => item.value = ''); + } + } else { + newDiscussionCommandId = useGuid().guid(); + discussionGroupCommand = { + id: newDiscussionCommandId, + code: `${viewModelInfo.id.replace(/-/g, '').replace('component', '').replace('viewmodel', '')}${commandInfo['handlerName']}1`, + name: commandInfo['name'], + params: commandInfo['params'], + handlerName: commandInfo['handlerName'], + cmpId: discussionGroupControllerWebCmdId + }; + viewModelInfo.commands.push(discussionGroupCommand); + } + return { commandId: newDiscussionCommandId, commandCode: discussionGroupCommand.code }; + } + return { commandId: '', commandCode: '' }; + } + /** + * 为命令添加构件引用 + */ + function addWebCmdFordiscussionGroupCommand(newCommandId, commandHandlerName) { + const webCmds = formSchemaUtils.getCommands(); + if (webCmds) { + let discussionGroupCmd = webCmds.find(cmd => cmd.id === discussionGroupControllerWebCmdId); + if (!discussionGroupCmd) { + discussionGroupCmd = { + id: discussionGroupControllerWebCmdId, + path: 'Gsp/Web/WebCmp/bo-webcmp/metadata/webcmd', + name: 'DiscussionGroupController.webcmd', + refedHandlers: [] + }; + formSchemaUtils.getCommands().push(discussionGroupCmd); + } + if (newCommandId) { + discussionGroupCmd.refedHandlers.push( + { + host: newCommandId, + handler: commandHandlerName + } + ); + } + } + } + /** + * 创建表格筛选查询相关命令 + * @param discussionGroupMetadata 讨论区元数据 + */ + function createDiscussionGroupCommand(discussionGroupMetadata) { + const targetComponentId = targetComponentInstance.belongedComponentId; + const viewModelId = formSchemaUtils.getViewModelIdByComponentId(targetComponentId); + const viewModel = formSchemaUtils.getViewModelById(viewModelId); + const waitForAddCommands = [ + { + handlerName: 'queryAtUsers', + name: "加载at用户", + params: [], + emitEvent: 'onQueryAtUsers' + }, { + handlerName: 'addComment', + name: "提交评论", + params: [ + { + name: 'id', + shownName: '单据编号', + value: '', + defaultValue: null + }, + { + name: 'summary', + shownName: '描述', + value: '', + defaultValue: null + }, + { + name: 'configId', + shownName: '配置ID', + value: '', + defaultValue: null + } + ], + emitEvent: 'onAddComment' + }, { + handlerName: 'queryAllOrgs', + name: "查询所有部门信息", + params: [], + emitEvent: 'onQueryAllOrgs' + }, { + handlerName: 'queryFrequentAtUsers', + name: "查询常用at用户", + params: [], + emitEvent: 'onQueryFrequentAtUsers' + }]; + waitForAddCommands.forEach(commandInfo => { + const { commandId, commandCode } = createSingleDiscussionGroupCommand(viewModel, commandInfo); + // 将命令绑定到组件上 + // @用户取数事件onQueryUserCommand 提交评论事件onAddCommentCommand 查询部门事件 onQueryAllOrgs 查询常用@用户事件 queryFrequentAtUsers + if (commandCode) { + discussionGroupMetadata[commandInfo['emitEvent']] = commandCode; + } + // 为命令添加构件引用 + if (commandId) { + addWebCmdFordiscussionGroupCommand(commandId, commandInfo['handlerName']); + } + }); + } + function createDiscussionEditor() { + // 1、组装筛选条容器和筛选条 + const { discussionEditor, discussionEditorContainer } = createDiscussionEditorContainer(); + + // 2、创建筛选条相关命令 + createDiscussionGroupCommand(discussionEditor); + + // 3、因为涉及到新增【DiscussionGroupController控制器】,所以这里需要获取控制器信息,方便交互面板展示命令数据 + const formCommandService = designerHostService?.useFormCommand; + if (formCommandService) { + formCommandService.checkCommands(); + } + return discussionEditorContainer; + } + + return { createDiscussionEditor }; +} diff --git a/packages/ui-vue/components/discussion-editor/src/composition/use-discussion-editor.ts b/packages/ui-vue/components/discussion-editor/src/composition/use-discussion-editor.ts index 3f3a43d76d753515a1f025e12ad927eae73edf77..3083ffb46bb2d7c90ba846d8ebc07824c74fa599 100644 --- a/packages/ui-vue/components/discussion-editor/src/composition/use-discussion-editor.ts +++ b/packages/ui-vue/components/discussion-editor/src/composition/use-discussion-editor.ts @@ -1,8 +1,8 @@ import { ref, SetupContext } from 'vue'; -import { DiscussionEditorProps } from '../../discussion-editor.props'; +import { DiscussionEditorProps } from '../discussion-editor.props'; export function useDiscussionEditor(props: DiscussionEditorProps, context: SetupContext) { - const personSearchUrl = ref(props.personSearchUrl); + const personSearchUrl = ref(); const personnelsDisplayKey = ref(props.personnelsDisplayKey); const relativeVisible = ref(false); /** 选择要发送的人员列表 */ @@ -22,14 +22,25 @@ export function useDiscussionEditor(props: DiscussionEditorProps, context: Setup } return array.findIndex(item => value === item[fieldInArray]) !== -1; } - function getSearchData(text: string, pageIndex: number):Promise { - if (personSearchUrl.value) { - const url = `${personSearchUrl.value}?param=${text}&pageSize=20&pageIndex=${pageIndex}`; - // return http.get(url); + /** + * 根据查询条件,分页信息 + * @param text + * @param pageIndex + * @returns + */ + async function getSearchData(text: string, pageIndex: number): Promise { + if (props.personSearchUrl) { + const url = `${props.personSearchUrl}?param=${text}&pageSize=20&pageIndex=${pageIndex}`; + return fetch(url).then(response => { + if (!response.ok) { + throw new Error('获取数据异常'); + } + return response.json(); + }); } return new Promise((resolve, reject) => { - resolve(true); - }); + resolve([]); + }); } /** 获得占位头像 */ function getAvatar(item: any) { @@ -40,8 +51,8 @@ export function useDiscussionEditor(props: DiscussionEditorProps, context: Setup return ''; } function setRelativeValue() { - personModalVisible.value = false; - relativeVisible.value = false; + // personModalVisible.value = false; + // relativeVisible.value = false; } return { @@ -55,6 +66,6 @@ export function useDiscussionEditor(props: DiscussionEditorProps, context: Setup _isInArray, getSearchData, getAvatar, - setRelativeValue, + setRelativeValue }; } diff --git a/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.component.tsx b/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1e011ab9ea3f9cc3aa6d125dad2f152cf83a4c34 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.component.tsx @@ -0,0 +1,55 @@ +import { SetupContext, computed, defineComponent, inject, onMounted, provide, ref, watch } from 'vue'; +import { DesignerItemContext } from '@farris/ui-vue/components/designer-canvas'; +import { useDesignerComponent } from '@farris/ui-vue/components/designer-canvas'; +import { DiscussionEditorDesignProps, discussionEditorDesignProps } from './discussion-editor.design.props'; +import { useDiscussionEditorRules } from './use-discussion-editor-rules'; +import { getCustomClass } from '@farris/ui-vue/components/common'; +export default defineComponent({ + name: 'FDiscussionEditorDesign', + props: discussionEditorDesignProps, + emits: [] as (string[] & ThisType) | undefined, + setup(props: DiscussionEditorDesignProps, context) { + + const elementRef = ref(); + const designItemContext = inject('design-item-context') as DesignerItemContext; + const designerHostService = inject('designer-host-service'); + const designerRulesComposition = useDiscussionEditorRules(designItemContext, designerHostService); + const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); + + const discussionEditorClass = computed(() => { + const classObject = { + 'f-discussion-group-edit': true, + 'ide-discussion-editor':true, + } as Record; + return getCustomClass(classObject, props.customClass); + }); + + onMounted(() => { + elementRef.value.componentInstance = componentInstance; + + }); + + context.expose(componentInstance.value); + + return () => { + return ( +
+
+
订金金额过低,是否该季商品正在打折促销 @丁洋
+
+ +
+ ); + }; + } +}); diff --git a/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.props.ts b/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..af0daf0f047d2594cc393689d7c5e65df44974ea --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/designer/discussion-editor.design.props.ts @@ -0,0 +1,15 @@ + +import { ExtractPropTypes} from "vue"; +import { createPropsResolver } from "../../../dynamic-resolver/src/props-resolver"; +import { schemaMapper } from '../schema/schema-mapper'; +import { schemaResolver } from '../schema/schema-resolver'; +import { discussionEditorProps } from "../discussion-editor.props"; +import discussionEditorrSchema from '../schema/discussion-editor.schema.json'; + +export const discussionEditorDesignProps = Object.assign({}, discussionEditorProps, { + componentId: { type: String, default: '' } +}); + +export type DiscussionEditorDesignProps = ExtractPropTypes; + +export const propsDesignResolver = createPropsResolver(discussionEditorDesignProps, discussionEditorrSchema, schemaMapper, schemaResolver); diff --git a/packages/ui-vue/components/discussion-editor/src/designer/use-discussion-editor-rules.ts b/packages/ui-vue/components/discussion-editor/src/designer/use-discussion-editor-rules.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d726dbf9276abfe0dcf618b7c624132214ac862 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/designer/use-discussion-editor-rules.ts @@ -0,0 +1,56 @@ +import { ComponentSchema, UseDesignerRules, DesignerItemContext } from "@farris/ui-vue/components/designer-canvas"; +import { DiscussionEditorProperty } from "../property-config/discussion-editor.property-config"; + + +export function useDiscussionEditorRules(designItemContext: DesignerItemContext, designerHostService): UseDesignerRules { + + // 构造属性配置方法 + function getPropsConfig(componentId: string) { + const schema = designItemContext.schema as ComponentSchema; + const discussionProps = new DiscussionEditorProperty(componentId, designerHostService); + return discussionProps.getPropertyConfig(schema); + } + + function canAccepts() { + return false; + } + function checkCanDeleteComponent() { + return false; + } + function checkCanMoveComponent() { + return false; + } + /** + * 组件删除后事件 + */ + function onRemoveComponent() { + // // 1、移除根节点对应的样式 + // if (designItemContext?.parent?.parent?.schema) { + // const sectionParentSchema = designItemContext?.parent?.parent.schema; + // if (sectionParentSchema.appearance?.class && sectionParentSchema.appearance?.class.includes('f-page-has-scheme')) { + // sectionParentSchema.appearance.class = sectionParentSchema.appearance?.class.replace('f-page-has-scheme', '').replace(' ', ''); + // } + // } + + // // 2、移除筛选方案相关的变量 + // const { formSchemaUtils } = designerHostService; + // const targetComponentId = designItemContext.componentInstance.value.belongedComponentId; + // const viewModelId = formSchemaUtils.getViewModelIdByComponentId(targetComponentId); + // const viewModel = formSchemaUtils.getViewModelById(viewModelId); + // if (viewModel && viewModel.states) { + // const filterVariableIndex = viewModel.states.findIndex(state => state.code === 'originalFilterConditionList'); + // if (filterVariableIndex > -1) { + // viewModel.states.splice(filterVariableIndex, 1); + // } + // } + } + + return { + getPropsConfig, + canAccepts, + checkCanDeleteComponent, + checkCanMoveComponent, + onRemoveComponent + } as UseDesignerRules; + +} diff --git a/packages/ui-vue/components/discussion-editor/src/discussion-editor.component.tsx b/packages/ui-vue/components/discussion-editor/src/discussion-editor.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..71a785065619e96367d1e6114ed693933a42f4a1 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/discussion-editor.component.tsx @@ -0,0 +1,252 @@ + +/* eslint-disable no-use-before-define */ +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { computed, defineComponent, onMounted, ref, SetupContext } from 'vue'; +import { discussionEditorProps, DiscussionEditorProps } from './discussion-editor.props'; +import { MsgInfo, editAttachFile } from './types/interface'; +import { useDiscussionEditor } from './composition/use-discussion-editor'; +import getColleagePanelRender from './components/colleague-panel.component'; +import { getCustomClass } from '@farris/ui-vue/components/common'; +import { FNotifyService } from "@farris/ui-vue/components/notify"; +import './discussion-editor.scss'; +import { LocaleService } from '../../locale'; +import FColleaguePanel from './components/colleague-panel.component'; + +export default defineComponent({ + name: 'FDiscussionEditor', + props: discussionEditorProps, + emits: ['selections', 'lineData', 'valueChange', 'filePreview', 'fileRemove', 'fileUploadDone', 'personnelSearch', 'getOutUsers'] as (string[] & ThisType) | undefined, + setup(props: DiscussionEditorProps, context: SetupContext) { + const notifyService: any = new FNotifyService(); + const personnelsPrimaryKey = ref(props.personnelsPrimaryKey); + const type = ref(props.type); + const orgUrl = ref(props.orgUrl); + const sectionData = ref(props.sectionData); + + let options: any; + + const discussionEditorClass = computed(() => { + const classObject = { + 'f-discussion-editor': true, + } as Record; + return getCustomClass(classObject, props.customClass); + }); + + /** 人事弹窗列表数据 */ + const _replyUser = ref(props.replyUser); + + const replyUser = computed({ + get() { + return _replyUser.value; + }, + set(val: any) { + if (val) { + _replyUser.value = val; + if (_replyUser.value.id) { + editorFocus(); + } + } + } + }); + + const _attachFiles = ref(props.attachFiles); + + const attachFiles = computed({ + get() { + return _attachFiles.value; + }, + set(val: any) { + if (val) { + _attachFiles.value = val; + } + } + }); + + /** 审批意见 */ + const textValue = ref(''); + + /** 暂存人员信息 */ + const tempPersonnelsValue = ref(''); + /** 暂存部门 */ + const tempSectionValue = ref(''); + + /** 选择要发送的部门 */ + let selectedSection: any = []; + + /** 上传附件是否显示 */ + const attachFilesModalVisible = ref(false); + + + const permissionList = [ + { value: 'ALL', text: LocaleService.getLocaleValue('discussionGroup.all') }, + { value: 'RELATED', text: LocaleService.getLocaleValue('discussionGroup.related') } + ]; + const permissionType = ref(props.permissionType); + + const { ...otherEditorInfo } = useDiscussionEditor(props, context); + onMounted(() => { + options = { maxUploads: 3, maxFileSize: 10240, allowedContentTypes: ['.jpg', '.pdf'] }; + } + ); + /** + * 检查权限 + * @returns + */ + function getPermissionInfo() { + const foundPermission = permissionList.find(item => item.value === permissionType.value); + return foundPermission ? foundPermission : permissionList[0]; + } + /** 文本框失去焦点触发 */ + function setTextValue(e: any) { + if (e) { + textValue.value = e.target.innerHTML; + } + if (tempPersonnelsValue.value) { + textValue.value += tempPersonnelsValue.value; + } + if (tempSectionValue.value) { + textValue.value += tempSectionValue.value; + } + tempPersonnelsValue.value = ''; + tempSectionValue.value = ''; + } + /** 监听键盘事件, 主要是用于删除@人 */ + function listenEditorValueChange(e: any) { + const { children } = e.target; + const childrenId: any = []; + for (let i = 0; i < children.length; i++) { + childrenId.push(children[i].id); + } + + otherEditorInfo.selectedPersonnels.forEach((personnel: any, index: any) => { + if (!childrenId.includes(personnel[personnelsPrimaryKey.value])) { + otherEditorInfo.selectedPersonnels.splice(index, 1); + } + }); + // selectedSection.forEach((section: any, index: any) => { + // if (!childrenId.includes(section[personnelsPrimaryKey.value])) { + // selectedSection.splice(index, 1); + // } + // }); + } + /** + * 提交评语 + */ + function submitApproval() { + if (!textValue.value) { + notifyService.error({ message: '提交内容不能为空', position: 'top-center' }); + return; + } + const editAttachFiles: editAttachFile[] = []; + if (attachFiles.value && attachFiles.value.length) { + attachFiles.value.forEach((file: any) => { + const { id } = file; + const { name } = file; + const { size } = file; + const { metadataId } = file.extend; + const attachFile = { + id, + name, + size, + metadataId + }; + editAttachFiles.push(attachFile); + }); + } + context.emit('valueChanage', { + msgInfo: MsgInfo.Confirm, + text: textValue.value, + mailTos: otherEditorInfo.selectedPersonnels, + mailToSections: selectedSection, + visibility: getPermissionInfo, + parentId: (replyUser.value && 'id' in replyUser.value) ? replyUser.value.id : null, + attachFiles: editAttachFiles.length ? editAttachFiles : null + }); + textValue.value = ''; + otherEditorInfo.selectedPersonnels = []; + selectedSection = []; + attachFiles.value = []; + replyUser.value = {}; + } + + function cancelApproval() { + context.emit('value', { + msgInfo: MsgInfo.Cancel, + text: null, + mailTos: [], + mailToSections: [], + visibility: null, + parentId: null, + attachFiles: null + }); + textValue.value = ''; + otherEditorInfo.selectedPersonnels= []; + selectedSection = []; + attachFiles.value = []; + replyUser.value = {}; + } + + const editor = ref(null); + /** 获得焦点 */ + function editorFocus() { + editor.value?.focus(); + } + function colleaguePanelSubmit(data) { + textValue.value = data; + } + return () => { + return props.visible ?
+ {(replyUser.value && replyUser.value.id) && ( +
+ 回复 + + {replyUser.value[props.replyPersonnelsDisplayKey]} + + : +
+ )} +
+
+
listenEditorValueChange(e)} + onBlur={(e) => setTextValue(e)} + ref={editor} + contenteditable={true} + innerHTML={textValue.value} + >
+
+ +
+
: null; + }; + } +}); diff --git a/packages/ui-vue/components/discussion-editor/discussion-editor.props.ts b/packages/ui-vue/components/discussion-editor/src/discussion-editor.props.ts similarity index 48% rename from packages/ui-vue/components/discussion-editor/discussion-editor.props.ts rename to packages/ui-vue/components/discussion-editor/src/discussion-editor.props.ts index 3d6064c1c35b05ba6572bacd7d69c05baedf3dd8..219d3ee66f26d804ab2ddf0a9d0afcf003e78995 100644 --- a/packages/ui-vue/components/discussion-editor/discussion-editor.props.ts +++ b/packages/ui-vue/components/discussion-editor/src/discussion-editor.props.ts @@ -13,21 +13,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; +// import { createPropsResolver } from "@farris/ui-vue/components/dynamic-resolver"; +import discussionEditorrSchema from './schema/discussion-editor.schema.json'; +import { schemaMapper } from './schema/schema-mapper'; +import { schemaResolver } from './schema/schema-resolver'; +import { createPropsResolver } from '../../dynamic-resolver/src/props-resolver'; +import { PermissionType } from './types/type'; export const discussionEditorProps = { + /** 发表评论的取消按钮是否可见 */ cancelVisible: { Type: Boolean, default: true }, + /** @人员时,区分人员的标识 */ personnelsPrimaryKey: { Type: String, default: 'id' }, + /** @人员时,展示的人员信息字段 */ personnelsDisplayKey: { Type: String, default: 'name' }, + /** 回复人员时,展示的回复人员信息字段 */ replyPersonnelsDisplayKey: { Type: String, default: 'userName' }, editHeight: { Type: Number, default: 130 }, type: { Type: String, default: 'user' }, + /** 组织查询链接,应用在组织人员窗口 */ orgUrl: { Type: String, default: '' }, + /** 人员查询链接,根据组织查询对应的人员 */ personSearchUrl: { Type: String, default: '' }, + /** 查询所有部门数据 */ sectionData: { Type: Object, default: [] }, + /** 回复人员数据信息 */ replyUser: { Type: Object, default: { id: {} } }, + /** 获取@用户|获取常用@用户 */ personnels: { Type: Object, default: [] }, + /** 评论附加的组件 */ attachFiles: { Type: Object, default: [] }, -}; + /** 弹出窗口的样式 */ + popupClass: { Type: String, default: '' }, + /** 控制是否可见 */ + visible: { type: Boolean, default: true }, + /** 组件自定义样式 */ + customClass: { type: String, default: '' }, + permissionType: { type: String as PropType, default: 'ALL' }, + id: { Type: String, default: '' } +} as Record; export type DiscussionEditorProps = ExtractPropTypes; +export const propsResolver = createPropsResolver(discussionEditorProps, discussionEditorrSchema, schemaMapper, schemaResolver); + diff --git a/packages/ui-vue/components/discussion-editor/discussion-editor.scss b/packages/ui-vue/components/discussion-editor/src/discussion-editor.scss similarity index 71% rename from packages/ui-vue/components/discussion-editor/discussion-editor.scss rename to packages/ui-vue/components/discussion-editor/src/discussion-editor.scss index a4c08327a488040f6d10533d296308ab3f3b55f7..da5cd67dc5315c22ae8db1c792dc1119bc58330a 100644 --- a/packages/ui-vue/components/discussion-editor/discussion-editor.scss +++ b/packages/ui-vue/components/discussion-editor/src/discussion-editor.scss @@ -66,151 +66,6 @@ margin-right: 12px; } - .f-discussion-group-edit-toolbar-about { - position: relative; - margin-right: 32px; - - .f-discussion-group-about-dropdown { - position: absolute; - top: calc(100% + 6px); - left: 0px; - width: 300px; - padding-top: 16px; - z-index: 999; - background: #fff; - box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.15); - border-radius: 2px; - - .f-discussion-group-about-dropdown-list { - max-height: 282px; - margin-top: 10px; - overflow-y: auto; - - &::-webkit-scrollbar { - width: 7px; - height: 7px; - background-color: #8e8e8e; - } - - &::-webkit-scrollbar-track { - border-radius: 0; - background-color: #fff; - border: 0; - background-clip: padding-box; - border-right: 0; - } - - &::-webkit-scrollbar-thumb { - border-radius: 0; - background-color: #dbdbdb; - border: 0; - background-clip: content-box; - opacity: .6; - transform: rotate(90deg); - border-radius: 3px; - } - - &-item { - padding: 3px 14px; - list-style: none; - align-items: center; - - .f-discussion-group-about-dropdown-list-detail { - margin-left: 6px; - - .about-list-detail-text { - line-height: 20px; - font-size: 12px; - color: #0A131A; - margin: 0 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .about-list-detail-subtext { - font-size: 12px; - color: #8F9CA6; - line-height: 20px; - } - } - - .f-icon-check { - border: 1px solid #d8d9d9; - color: #fff; - font-size: 15px; - border-radius: 2px; - - &::before { - content: ''; - } - } - - &.active .f-icon-check { - border: none; - background: #4c91ff; - - &::before { - content: '\e118'; - } - } - - .about-list-item-avatar { - display: block; - width: 30px; - height: 30px; - margin-left: 6px; - border-radius: 50%; - overflow: hidden; - } - - .about-list-item-avatar-tip { - width: 32px; - height: 32px; - margin-left: 6px; - font-size: 12px; - color: #fff; - text-align: center; - line-height: 32px; - border-radius: 50%; - background-color: #4796FF; - } - - &:hover { - background: #E6F7FF; - } - - &.about-dropdown-list-item-empty { - cursor: default; - - &:hover { - background: #fff; - } - } - } - } - - .f-discussion-group-about-search { - padding: 0 14px; - } - - .f-about-list-btns { - justify-content: space-between; - background: #faf9f9; - border-top: 1px solid #efefef; - padding: 10px 15px; - - .btn { - margin-right: 10px; - - &:last-child { - margin-right: 0; - } - } - } - } - } - .f-discussion-group-edit-toolbar-dropdown { position: relative; @@ -525,7 +380,7 @@ } .f-discussion-personnel-empty { - padding: 3px 14px; + padding: 3px 14px 3px 0; list-style: none; -webkit-box-align: center; align-items: center; @@ -662,4 +517,119 @@ color: rgba(0, 0, 0, 0.85); } } +} + + +.f-discussion-group-edit-toolbar-about { + .f-discussion-group-about-dropdown { + width: 300px; + padding-top: 16px; + + .f-discussion-group-about-dropdown-list { + max-height: 282px; + margin-top: 10px; + margin-bottom: 10px; + overflow-y: auto; + + &-item { + padding: 3px 14px 3px 0; + list-style: none; + align-items: center; + + .f-discussion-group-about-dropdown-list-detail { + margin-left: 6px; + + .about-list-detail-text { + line-height: 20px; + font-size: 12px; + color: #0A131A; + margin: 0 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .about-list-detail-subtext { + font-size: 12px; + color: #8F9CA6; + line-height: 20px; + } + } + + .f-icon-check { + border: 1px solid #d8d9d9; + color: #fff; + font-size: 15px; + border-radius: 2px; + + &::before { + content: ''; + } + } + + &.active .f-icon-check { + border: none; + background: #4c91ff; + + &::before { + content: '\e118'; + } + } + + .about-list-item-avatar { + display: block; + width: 30px; + height: 30px; + margin-left: 6px; + border-radius: 50%; + overflow: hidden; + } + + .about-list-item-avatar-tip { + width: 32px; + height: 32px; + margin-left: 6px; + font-size: 12px; + color: #fff; + text-align: center; + line-height: 32px; + border-radius: 50%; + background-color: #4796FF; + } + + &:hover { + background: #E6F7FF; + } + + &.about-dropdown-list-item-empty { + cursor: default; + text-align: center; + + &:hover { + background: #fff; + } + } + } + } + + .f-discussion-group-about-search { + padding: 0 14px; + margin:0 0 14px; + } + + .f-about-list-btns { + justify-content: space-between; + background: #faf9f9; + border-top: 1px solid #efefef; + padding: 10px 15px; + + .btn { + margin-right: 10px; + + &:last-child { + margin-right: 0; + } + } + } + } } \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-editor/src/property-config/discussion-editor.property-config.ts b/packages/ui-vue/components/discussion-editor/src/property-config/discussion-editor.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..1dfc737cf3c84cf62583c3e42da0b8077c88b762 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/property-config/discussion-editor.property-config.ts @@ -0,0 +1,85 @@ +import { BaseControlProperty } from "@farris/ui-vue/components/property-panel"; + +export class DiscussionEditorProperty extends BaseControlProperty { + constructor(componentId: string, designerHostService: any) { + super(componentId, designerHostService); + } + public getPropertyConfig(propertyData: any) { + // 基本信息 + this.propertyConfig.categories['basic'] = this.getBasicPropConfig(propertyData); + // 外观 + this.propertyConfig.categories['appearance'] = this.getAppearanceConfig(propertyData); + // 行为 + this.getBehaviorConfig(); + // 事件交互面板 + this.getEventPropConfig(propertyData); + + return this.propertyConfig; + } + + getBehaviorConfig() { + this.propertyConfig.categories['behavior'] = { + title: "行为", + description: "Behavior", + properties: { + editHeight: { + title: "编辑器高度", + description: '指定讨论编辑器的高度', + type: 'number', + editor: { + nullable: true, + min: 0, + useThousands: false + } + }, + personSearchUrl: { + title: "人员查询地址", + description: '指定人员查询时服务器端的地址', + type: 'string' + } + } + }; + } + private getEventPropConfig(propertyData: any) { + const events = [ + { + "label": "onQueryAtUsers", + "name": "@用户取数事件" + }, + { + "label": "onAddComment", + "name": "提交评论事件" + }, + { + "label": "onQueryAllOrgs", + "name": "查询部门事件" + }, + { + "label": "onQueryFrequentAtUsers", + "name": "查询常用@用户事件" + } + ]; + const self = this; + const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); + const properties = self.createBaseEventProperty(initialData); + + this.propertyConfig.categories['eventsEditor'] = { + title: '事件', + hideTitle: true, + properties, + // 这个属性,标记当属性变更得时候触发重新更新属性 + refreshPanelAfterChanged: true, + tabId: 'commands', + tabName: '交互', + setPropertyRelates(changeObject: any, data: any) { + const parameters = changeObject.propertyValue; + delete propertyData[self.viewModelId]; + if (parameters) { + parameters.setPropertyRelates = this.setPropertyRelates; // 添加自定义方法后,调用此回调方法,用于处理联动属性 + self.eventsEditorUtils.saveRelatedParameters(propertyData, self.viewModelId, parameters['events'], parameters); + } + } + }; + } + +} diff --git a/packages/ui-vue/components/discussion-editor/src/schema/discussion-editor.schema.json b/packages/ui-vue/components/discussion-editor/src/schema/discussion-editor.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..e1f7381a73e8279636d98d30fc650fc36a679b7b --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/schema/discussion-editor.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://farris-design.gitee.io/discussion-editor.schema.json", + "title": "discussion-editor", + "description": "A Farris Component", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for discussion-editor", + "type": "string" + }, + "type": { + "description": "The type string of discussion-editor", + "type": "string", + "default": "discussion-editor" + }, + "appearance": { + "description": "", + "type": "object", + "properties": { + "class": { + "type": "string" + }, + "style": { + "type": "string" + } + }, + "default": {} + }, + "personSearchUrl": { + "description": "", + "type": "string" + }, + "editHeight": { + "description": "", + "type": "number" + } + }, + "required": [ + "id", + "type" + ] +} \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-editor/src/schema/schema-mapper.ts b/packages/ui-vue/components/discussion-editor/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..97964aee23bbb8b523c7692723ea02db00d4f4c0 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '../../../dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/discussion-editor/src/schema/schema-resolver.ts b/packages/ui-vue/components/discussion-editor/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..c43ff6d0420e5f18628377f0ecfc8bd3ad04e8a1 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/schema/schema-resolver.ts @@ -0,0 +1,15 @@ +import { DesignerComponentInstance, DesignerHostService } from "@farris/ui-vue/components/designer-canvas"; +import { DynamicResolver } from "@farris/ui-vue/components/dynamic-resolver"; +import { buildDiscussionEditor } from "../composition/build-discussion-editor"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record, designerHostService?: DesignerHostService): Record { + const parentComponentInstance = context.parentComponentInstance as DesignerComponentInstance; + if (parentComponentInstance && designerHostService) { + const filterBarCreator = buildDiscussionEditor(context, designerHostService); + const filterBarContainer = filterBarCreator.createDiscussionEditor(); + if (filterBarContainer) { + return filterBarContainer; + } + } + return schema; +} diff --git a/packages/ui-vue/components/discussion-editor/src/types/composition.ts b/packages/ui-vue/components/discussion-editor/src/types/composition.ts new file mode 100644 index 0000000000000000000000000000000000000000..e0142132232bb24f64a4bb1f027528a1ce17f341 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/types/composition.ts @@ -0,0 +1,8 @@ +import { Ref } from "vue"; + +export interface UsePopup { + + hidePopup(): void; + + popoverRef: Ref; +} \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-editor/src/types/type.ts b/packages/ui-vue/components/discussion-editor/src/types/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..d87bcb40d55d8f8e907934ca88cea61976366d43 --- /dev/null +++ b/packages/ui-vue/components/discussion-editor/src/types/type.ts @@ -0,0 +1 @@ +export type PermissionType = 'ALL' | 'RELATED' ; \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-list/index.ts b/packages/ui-vue/components/discussion-list/index.ts index 4b75135d76faab05bdf91a3c1940c027a2e7996b..a43ff20dcf2c01200e6e28c63687d886a6aeb4b4 100644 --- a/packages/ui-vue/components/discussion-list/index.ts +++ b/packages/ui-vue/components/discussion-list/index.ts @@ -13,15 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { App } from 'vue'; -import DiscussionListProps from './discussion-list.component'; +import { withInstall } from '@farris/ui-vue/components/common'; +import FDiscussionList from './src/discussion-list.component'; +export * from './src/discussion-list.props'; -export * from './discussion-list.props'; - -export { DiscussionListProps }; +FDiscussionList.register = ( + componentMap: Record, propsResolverMap: Record, + configResolverMap: Record, resolverMap: Record): void => { + componentMap['discussion-list'] = FDiscussionList; + // propsResolverMap['discussion-editor'] = propsResolver; +}; -export default { - install(app: App): void { - app.component(DiscussionListProps.name as string, DiscussionListProps); - } +FDiscussionList.registerDesigner = (componentMap: Record, propsResolverMap: Record, + configResolverMap: Record): void => { + componentMap['discussion-list'] = FDiscussionList; }; + +export { FDiscussionList }; + +export default withInstall(FDiscussionList); \ No newline at end of file diff --git a/packages/ui-vue/components/discussion-list/discussion-list.component.tsx b/packages/ui-vue/components/discussion-list/src/discussion-list.component.tsx similarity index 100% rename from packages/ui-vue/components/discussion-list/discussion-list.component.tsx rename to packages/ui-vue/components/discussion-list/src/discussion-list.component.tsx diff --git a/packages/ui-vue/components/discussion-list/discussion-list.props.ts b/packages/ui-vue/components/discussion-list/src/discussion-list.props.ts similarity index 100% rename from packages/ui-vue/components/discussion-list/discussion-list.props.ts rename to packages/ui-vue/components/discussion-list/src/discussion-list.props.ts diff --git a/packages/ui-vue/components/discussion-list/discussion-list.scss b/packages/ui-vue/components/discussion-list/src/discussion-list.scss similarity index 100% rename from packages/ui-vue/components/discussion-list/discussion-list.scss rename to packages/ui-vue/components/discussion-list/src/discussion-list.scss diff --git a/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json b/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json index 7583ec2dbfa816d215948b275bd510b0b876016d..18d6510bf30a73a4911b0c5b04f5553d3f6775f6 100644 --- a/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json +++ b/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json @@ -73,8 +73,7 @@ }, "showLabelType":{ "description": "", - "type": "string", - "default": "visible" + "type": "string" } }, "required": [ diff --git a/packages/ui-vue/components/dynamic-form/src/types.ts b/packages/ui-vue/components/dynamic-form/src/types.ts index 4620dc881d3bcb8b02604bad61381b33e70fd66c..2e2c1a839a8a08432fef36225bc721c185874579 100644 --- a/packages/ui-vue/components/dynamic-form/src/types.ts +++ b/packages/ui-vue/components/dynamic-form/src/types.ts @@ -4,7 +4,7 @@ export type EditorType = 'button-edit' | 'check-box' | 'check-group' | 'combo-li 'response-layout-editor-setting' | 'switch' | 'grid-field-editor' | 'field-selector' | 'schema-selector' | 'mapping-editor' | 'textarea' | 'response-form-layout-setting' | 'binding-selector' | 'query-solution-config' | 'solution-preset' | 'filter-bar-config'|'item-collection-editor' | 'menu-lookup' | 'response-layout-splitter' | 'json-editor' | 'property-editor' | 'sort-condition-editor' | - 'filter-condition-editor' | 'expression-editor' | 'code-editor' | 'collection-property-editor' | 'language-textbox' | 'rich-text-editor'; + 'filter-condition-editor' | 'expression-editor' | 'code-editor' | 'collection-property-editor' | 'language-textbox'| 'image' | 'rich-text-editor'; export interface EditorConfig { /** 编辑器类型 */ diff --git a/packages/ui-vue/components/dynamic-view/src/components/maps.ts b/packages/ui-vue/components/dynamic-view/src/components/maps.ts index f54e59f95445de479a7c83c5abc11ebb8da447e8..c60b3422377cfcc487fd15a4cde60b46ea7f6960 100644 --- a/packages/ui-vue/components/dynamic-view/src/components/maps.ts +++ b/packages/ui-vue/components/dynamic-view/src/components/maps.ts @@ -73,7 +73,9 @@ import FCollectionPropertyEditor from '@farris/ui-vue/components/collection-prop import FModal from '@farris/ui-vue/components/modal'; import FExternalContainer from '@farris/ui-vue/components/external-container'; import FLanguageTextbox from '@farris/ui-vue/components/language-textbox'; - +import FImage from '@farris/ui-vue/components/image'; +import FDiscussionEditor from '@farris/ui-vue/components/discussion-editor'; +import FDiscussionList from '@farris/ui-vue/components/discussion-list'; import { createPropsResolver } from '@farris/ui-vue/components/dynamic-resolver'; import { useThirdComponent } from '@farris/ui-vue/components/common'; @@ -167,7 +169,9 @@ async function loadRegister() { FModal.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); FExternalContainer.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); FLanguageTextbox.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); - + FImage.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FDiscussionEditor.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FDiscussionList.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); // FHtmlEditor.createPropsResolver = createPropsResolver; // FHtmlEditor.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); const thirdComponents = window[globalStorageKey]; diff --git a/packages/ui-vue/components/image/index.ts b/packages/ui-vue/components/image/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9fe480907b8d55050b2c73dfec8acd112332db75 --- /dev/null +++ b/packages/ui-vue/components/image/index.ts @@ -0,0 +1,36 @@ + + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import FImage from './src/image.component'; +import { propsResolver } from './src/image.props'; +import FImageDesign from './src/designer/image.design.component'; +import { withInstall } from '@farris/ui-vue/components/common'; + +export * from './src/image.props'; + +export { FImage }; + +FImage.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { + componentMap['image'] = FImage; + propsResolverMap['image'] = propsResolver; +}; +FImage.registerDesigner = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record) => { + componentMap['image'] = FImageDesign; + propsResolverMap['image'] = propsResolver; +}; + +export default withInstall(FImage); diff --git a/packages/ui-vue/components/image/src/designer/image.design.component.tsx b/packages/ui-vue/components/image/src/designer/image.design.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0222b013c1e28551019e58a9dcaafee559ff1500 --- /dev/null +++ b/packages/ui-vue/components/image/src/designer/image.design.component.tsx @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineComponent, computed, ref, SetupContext, inject, onMounted } from 'vue'; +import { imageDesignProps, ImageDesignProps } from '../image.props'; +import { DesignerItemContext, useDesignerComponent } from '@farris/ui-vue/components/designer-canvas'; +import { useImageDesignerRules } from './use-rules'; +import { getCustomClass } from '@farris/ui-vue/components/common'; + +export default defineComponent({ + name: 'FImageDesign', + props: imageDesignProps, + emits: ['change', 'update:modelValue'] as (string[] & ThisType) | undefined, + setup(props: ImageDesignProps, context: SetupContext) { + const elementRef = ref(); + const designerHostService = inject('designer-host-service'); + const designItemContext = inject('design-item-context') as DesignerItemContext; + const designerRulesComposition = useImageDesignerRules(designItemContext, designerHostService); + const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); + + onMounted(() => { + elementRef.value.componentInstance = componentInstance; + }); + + context.expose(componentInstance.value); + + const imageClass = computed(() => { + const classObject = { + 'f-imgcontainer-in-form': true, + }; + return getCustomClass(classObject, props.customClass); + }); + + const imageStyle = computed(() => { + const customStyle = {}; + if (props.width && props.width > 0) { + customStyle['width'] = props.width + 'px'; + } + if (props.height && props.height > 0) { + customStyle['height'] = props.height + 'px'; + } else { + customStyle['height'] = '100px'; + } + return customStyle; + }); + + const designImageSrc = "data:image/jpeg;base64,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"; + + return () => ( +
+ {props.alt} +
+ ); + } +}); diff --git a/packages/ui-vue/components/image/src/designer/use-rules.ts b/packages/ui-vue/components/image/src/designer/use-rules.ts new file mode 100644 index 0000000000000000000000000000000000000000..3aac5efdef9836b0ab2b7557f179a7a4c0ed71de --- /dev/null +++ b/packages/ui-vue/components/image/src/designer/use-rules.ts @@ -0,0 +1,20 @@ + +import { UseDesignerRules } from "../../../designer-canvas/src/composition/types"; +import { ComponentSchema, DesignerComponentInstance, DesignerItemContext } from "../../../designer-canvas/src/types"; +import { ImageProperty } from "../property-config/image.property-config"; + +export function useImageDesignerRules(designItemContext: DesignerItemContext, designerHostService): UseDesignerRules { + + const schema = designItemContext.schema as ComponentSchema; + + // 构造属性配置方法 + function getPropsConfig(componentId: string, componentInstance: DesignerComponentInstance) { + const inputGroupProps = new ImageProperty(componentId, designerHostService); + return inputGroupProps.getPropertyConfig(schema, componentInstance); + } + + return { + getPropsConfig + } as UseDesignerRules; + +} diff --git a/packages/ui-vue/components/image/src/image.component.tsx b/packages/ui-vue/components/image/src/image.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4afecf9a53f5d85f027ec1ece3554cb0a11b267c --- /dev/null +++ b/packages/ui-vue/components/image/src/image.component.tsx @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineComponent, computed, ref, SetupContext, watch } from 'vue'; +import { imageProps, ImageProps } from './image.props'; +import { getCustomClass } from '@farris/ui-vue/components/common'; + +export default defineComponent({ + name: 'FImage', + props: imageProps, + emits: ['change', 'update:modelValue'] as (string[] & ThisType) | undefined, + setup(props: ImageProps, context: SetupContext) { + const imageClass = computed(() => { + const classObject = { + 'f-imgcontainer-in-form': true + }; + return getCustomClass(classObject, props.customClass); + }); + const imageStyle = computed(() => { + const customStyle = {}; + if (props.width && props.width > 0) { + customStyle['width'] = props.width + 'px'; + } + if (props.height && props.height > 0) { + customStyle['height'] = props.height + 'px'; + } + return customStyle; + }); + const defaultErrorImage = props.errorSrc ? props.errorSrc : 'data:image/png;base64,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'; + const imageSrc = ref(props.src); + + // 新路径 + watch(() => props.src, (newValue) => { + imageSrc.value = newValue; + }); + + function updateSrc(srcAddress) { + imageSrc.value = srcAddress; + } + + function errorSrc() { + imageSrc.value = defaultErrorImage; + } + context.expose({ updateSrc }); + + return () => { + return ( +
+ {props.alt} +
+ ); + }; + } +}); diff --git a/packages/ui-vue/components/image/src/image.props.ts b/packages/ui-vue/components/image/src/image.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3f68c9aad9c4d1ebbe1c2573e4f9d9e0ad745cd --- /dev/null +++ b/packages/ui-vue/components/image/src/image.props.ts @@ -0,0 +1,50 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ExtractPropTypes, PropType } from 'vue'; +import { createPropsResolver } from '../../dynamic-resolver/src/props-resolver'; +import { schemaResolver } from './schema/schema-resolver'; +import { schemaMapper } from './schema/schema-mapper'; +import imageSchema from './schema/image.schema.json'; + + +export const imageProps = { + // 绑定特殊字段,辅助构造src,指定服务器端图像Id + modelValue: { type: String, default: '' }, + // 服务器端根目录 + rootId: { type: String, default: 'default-root' }, + // 指定直接路径 + src: { type: String, default: '' }, + errorSrc: { type: String, default: '' }, + alt: { type: String, default: '' }, + title:{ type: String, default: '' }, + // 指定自定义样式 + customClass: { type: Object, default: '' }, + id: { type: String, default: '' }, + width: { type: Number }, + height: { type: Number }, + // 是否只读 + readonly: { type: Boolean, default: false } +} as Record; + +export type ImageProps = ExtractPropTypes; + +export const propsResolver = createPropsResolver(imageProps, imageSchema, schemaMapper, schemaResolver); + +export const imageDesignProps = Object.assign({}, imageProps, { + componentId: { type: String, default: '' } +}); +export type ImageDesignProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/image/src/property-config/image.property-config.ts b/packages/ui-vue/components/image/src/property-config/image.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..b54fb1af9a834650b97178bc49e16bdd3e64a594 --- /dev/null +++ b/packages/ui-vue/components/image/src/property-config/image.property-config.ts @@ -0,0 +1,74 @@ +import { DesignerComponentInstance } from "@farris/ui-vue/components/designer-canvas"; +import { InputBaseProperty } from "../../../property-panel/src/composition/entity/input-base-property"; + +export class ImageProperty extends InputBaseProperty { + + constructor(componentId: string, designerHostService: any) { + super(componentId, designerHostService); + } + getPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance) { + this.getCommonPropertyConfig(propertyData, componentInstance, 'Card'); + // 编辑器 + this.propertyConfig.categories['editor'] = this.getEditorProperties(propertyData); + return this.propertyConfig; + } + getGridFieldEdtiorPropConfig(propertyData: any, componentInstance: DesignerComponentInstance | null) { + this.propertyConfig.categories = {}; + this.getCommonPropertyConfig(propertyData, componentInstance, 'Grid'); + // 编辑器 + this.propertyConfig.categories['editor'] = this.getEditorProperties(propertyData); + return this.propertyConfig.categories; + } + getEditorProperties(propertyData) { + return { + description: "编辑器", + title: "编辑器", + type: "avatar", + $converter: "/converter/property-editor.converter", + parentPropertyID: "editor", + properties: { + rootId: { + description: "服务器根目录的Id", + title: "服务器根目录", + type: "string" + }, + src: { + description: "设置图片地址", + title: "图片地址", + type: "string" + }, + width: { + description: "", + title: "指定图像宽度", + type: "number", + editor:{ + nullable:true + } + }, + height: { + description: "", + title: "指定图像宽度", + type: "number", + editor:{ + nullable:true + } + }, + errorSrc: { + description: "加载图像错误的替代图片", + title: "加载图像错误的替代图片", + type: "string" + }, + alt: { + description: "图片未找到时,替代文本设置", + title: "替代文本", + type: "string" + }, + title: { + description: "鼠标滑过图像时的提示文本", + title: "提示文本", + type: "string" + } + } + }; + } +} diff --git a/packages/ui-vue/components/image/src/schema/image.schema.json b/packages/ui-vue/components/image/src/schema/image.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..8ef6dbbd2d9937a3b48f7bef2ac3c69fa7e61dd4 --- /dev/null +++ b/packages/ui-vue/components/image/src/schema/image.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://farris-design.gitee.io/image.schema.json", + "title": "image", + "description": "A Farris Component", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for image", + "type": "string" + }, + "type": { + "description": "The type string of image", + "type": "string", + "default": "image" + }, + "appearance": { + "description": "", + "type": "object", + "properties": { + "class": { + "type": "string" + }, + "style": { + "type": "string" + } + }, + "default": {} + }, + "binding": { + "description": "", + "type": "object", + "default": {} + }, + "rootId": { + "description": "", + "type": "string", + "default": "default-root" + }, + "src": { + "description": "", + "type": "string", + "default": "" + }, + "errorSrc": { + "description": "", + "type": "string", + "default": "" + }, + "alt": { + "description": "", + "type": "string", + "default": "" + }, + "title": { + "description": "", + "type": "string", + "default": "" + }, + "visible": { + "description": "", + "type": "boolean", + "default": true + } + }, + "required": [ + "id", + "type" + ], + "ignore": [ + "id", + "appearance", + "binding", + "visible" + ] +} \ No newline at end of file diff --git a/packages/ui-vue/components/image/src/schema/schema-mapper.ts b/packages/ui-vue/components/image/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ca33ca080647b9edc66a133396282d6a3b1698f --- /dev/null +++ b/packages/ui-vue/components/image/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '@farris/ui-vue/components/dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/image/src/schema/schema-resolver.ts b/packages/ui-vue/components/image/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..d36ae7457434b7381b796814e9926d352feacff6 --- /dev/null +++ b/packages/ui-vue/components/image/src/schema/schema-resolver.ts @@ -0,0 +1,5 @@ +import { DynamicResolver } from "@farris/ui-vue/components/dynamic-resolver"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { + return schema; +} diff --git a/packages/ui-vue/components/image/style.ts b/packages/ui-vue/components/image/style.ts new file mode 100644 index 0000000000000000000000000000000000000000..236d30166cfb3a58f75d49657a3778bc693fae22 --- /dev/null +++ b/packages/ui-vue/components/image/style.ts @@ -0,0 +1,2 @@ +import "@farris/ui-vue/components/dependent-base/style"; +import "@farris/ui-vue/components/dependent-icon/style"; diff --git a/packages/ui-vue/components/index.ts b/packages/ui-vue/components/index.ts index aac1ce65d31375384cb83e65e61b74f09eaa155c..fb75d175d38d3d9a7f36c0c3398752da3badcd08 100644 --- a/packages/ui-vue/components/index.ts +++ b/packages/ui-vue/components/index.ts @@ -16,6 +16,7 @@ import { App } from 'vue'; import Accordion from './accordion'; import Avatar from './avatar'; +import Image from './image'; import BorderEditor from './border-editor'; import Button from './button'; import ButtonGroup from './button-group'; @@ -113,6 +114,7 @@ export default { app.use(Locale, options) .use(Accordion) .use(Avatar) + .use(Image) .use(BorderEditor) .use(Button) .use(ButtonGroup) diff --git a/packages/ui-vue/components/list-view/src/list-view.component.tsx b/packages/ui-vue/components/list-view/src/list-view.component.tsx index b34bbde0527f58072279b40023b1c9df79dab834..a4e3dcf5aca61524bac968004835b8d831bfe8c0 100644 --- a/packages/ui-vue/components/list-view/src/list-view.component.tsx +++ b/packages/ui-vue/components/list-view/src/list-view.component.tsx @@ -250,7 +250,7 @@ export default defineComponent({ context.slots.footer && context.slots.footer() }} { - // shouldRenderPagination.value && renderDataGridPagination() + shouldRenderPagination.value && renderDataGridPagination() } ); diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts index 15d34717d17c8563da36ec9ec1f439a5254c2cab..77982725a3a92bbfdbeba210606d98d306c7acea 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts @@ -23,7 +23,7 @@ export class InputBaseProperty extends BaseControlProperty { super(componentId, designerHostService); this.responseLayoutEditorFunction = useResponseLayoutEditorSetting(this.formSchemaUtils); } - private getCommonPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null, showPosition = 'Card') { + public getCommonPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null, showPosition = 'Card') { // 基本信息 this.propertyConfig.categories['basic'] = this.getBasicProperties(propertyData, componentInstance, showPosition); // 外观 @@ -97,7 +97,8 @@ export class InputBaseProperty extends BaseControlProperty { type: "enum", editor: { data: [{ "id": "visible", "name": "显示" }, { "id": "reserve-space", "name": "占位" }, { "id": "none", "name": "不显示" }] - } + }, + defaultValue: propertyData.editor?.type === 'image'?'none':'visible' }, binding: { description: "绑定的表单字段", diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts index 737fbc7f090b936b4280b885cabfb910d9d87e44..5a5e55afe5eebdec2053a849d83cca8d18bac729 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts @@ -11,6 +11,7 @@ export class SchemaDOMMapping { String: [ { key: DgControl['input-group'].type, value: DgControl['input-group'].name }, { key: DgControl['lookup'].type, value: DgControl['lookup'].name }, + { key: DgControl['image'].type, value: DgControl['image'].name }, { key: DgControl['date-picker'].type, value: DgControl['date-picker'].name }, { key: DgControl['switch'].type, value: DgControl['switch'].name }, { key: DgControl['check-box'].type, value: DgControl['check-box'].name }, @@ -23,7 +24,8 @@ export class SchemaDOMMapping { Text: [ { key: DgControl['textarea'].type, value: DgControl['textarea'].name }, { key: DgControl['lookup'].type, value: DgControl['lookup'].name }, - { key: DgControl['rich-text-editor'].type, value: DgControl['rich-text-editor'].name }, + { key: DgControl['image'].type, value: DgControl['image'].name }, + { key: DgControl['rich-text-editor'].type, value: DgControl['rich-text-editor'].name } ], Decimal: [ { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } diff --git a/packages/ui-vue/components/query-solution/src/composition/build-solution.ts b/packages/ui-vue/components/query-solution/src/composition/build-solution.ts index 5b4c53006a3c7899de0053fce1165c8a6a643f1e..18d1ad3ad0fbd5e9e4628ea376e924ed0c7c106d 100644 --- a/packages/ui-vue/components/query-solution/src/composition/build-solution.ts +++ b/packages/ui-vue/components/query-solution/src/composition/build-solution.ts @@ -292,7 +292,6 @@ export function querySolutionCreatorService(context: Record, design filterText: '筛选' }); - // 2、关联DOM处理--新增样式 const targetContainer = targetComponentInstance.schema; if (targetContainer) { diff --git a/packages/ui-vue/demos/discussion-editor/basic.vue b/packages/ui-vue/demos/discussion-editor/basic.vue index 847155a1de63ac791d8f5e2f125678fb2125802c..28693b95d2d272a040e6c50597eb41da4e5b1603 100644 --- a/packages/ui-vue/demos/discussion-editor/basic.vue +++ b/packages/ui-vue/demos/discussion-editor/basic.vue @@ -1,6 +1,54 @@ diff --git a/packages/ui-vue/demos/filter-bar/disabled.vue b/packages/ui-vue/demos/filter-bar/disabled.vue index d551e86e2f08c63b23e43b642749f5efadcbccb8..5396bbf9c623a8a4941a74554d4adeefc58c0a56 100644 --- a/packages/ui-vue/demos/filter-bar/disabled.vue +++ b/packages/ui-vue/demos/filter-bar/disabled.vue @@ -247,9 +247,6 @@ const conditions: any[] = [ } ] const disableState = ref(true) -watch(disableState,(newValue)=>{ - debugger; -})