页面树结构

版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

...

为了支持用户可以设置事件和操作的绑定关系,需要增加两个属性

  • 事件清单(eventRange):表示预置的时间类型,是一个数组。事件清单(eventRange):表示预置的事件类型,是一个数组。
  • 事件列表(events):用于存储用户设置的事件和操作的绑定。

...

代码块
languagejs
	// Vue数据绑定的时候要求返回的结果
	data() {
		return {
			// 插件的名字
			name: "simpleCellGroup",
			// 表示是否已经进入画布区
			t_preview: falsetrue,
			// 是否显示控件的属性编辑区
			t_edit: false,
			// 属性配置项,按需设置
			args: {
				// 前面已经描述,此处省略...
			},
			// 需要显示数据的集合,包括标题,图标,单元标题,单元内容,约定格式如下
			cellData: {},
		};
	},

...

代码块
	mounted() {
		// 生命周期函数,实现数据加载
		this.freshData();
	},
	methods: {
		// 使用 `handleQueryData` 获取数据
		...mapActions("DWF_form", ["handleQueryData"]),

		// 此处省略若干函数的实现直接接入正题...

		freshData() {
			// 获取已经绑定的实体类、关联类类名,此类名会有一个后缀以便表明其类型,例如实体类:Asset&e,关联类:PartToPart&r
			const bindTargetClass = this.args.bindTargetClass;
			if (!bindTargetClass) {
				console.error("no bindTargetClass");
				return;
			}
			// 在使用的时候,需要将后缀抹去,利用正则表达式实现如下			
			const regexForTargetClass = /^([0-9a-zA-Z_]+)&([er])$/;
			const glob = regexForTargetClass.exec(bindTargetClass);
			if (glob === null) {
				console.error(
					`handleRefresh: invalid targetClass value -> ${bindTargetClass}`
				);
				return;
			}

			// 经过正则过滤后,得到干净的实体类经过正则过滤后,得到去除后缀的实体类/关联类的类名
			let targetClass = glob[1];
			let query = {
				query: this.args.filterQuery,
				pageSize: 4, //设计的时候可以根据需要设置翻页和大小
				startIndex: 0,
			};

			this.handleQueryData({
				targetClass: targetClass,
				query: query,
				fresh: true,
			}).then((res) => {
				const attributeForTitle = this.args.attributeForTitle;
				const attributeForLabel = this.args.attributeForLabel;
				let objs = res;
				this.cellData = {
					title: this.args.label,
					icon: "ios-bookmarks-outline",
					list: res,
				};
			});
		},
	},
};

...

代码块
languagejs
titlepart-web/modeler/forms/simpleCellGroup.vue
collapsetrue
<template>
	<!--
    建模时的预览前端,即插件的实际显示样式
    :addinName="name"和ref="main"一般情况不可去除
	-->
	<section v-if="t_preview" :addinName="name" ref="main">
		<!-- 在画布区的显示内容 -->
		<span>
			<!-- see https://www.iviewui.com/components/cell#JCYF -->
			<Card :style="cardStyle" class="cell-group-wrapper">
				<p slot="title">
					<!-- change to slot syntax to avoid a bug, where :title may not respond to change as a prop. -->
					<Icon :type="cellData.icon"></Icon>
					{{ cellData.title }}
				</p>
				<CellGroup v-if="cellData.list && cellData.list.length">
					<Cell
						v-for="obj in cellData.list"
						:key="obj.oid"
						:title="String(obj[args.attributeForTitle])"
						:label="String(obj[args.attributeForLabel])"
					>
						<Avatar :src="String(obj[args.attributeForIcon])" slot="icon" />
					</Cell>
				</CellGroup>
				<div v-else class="no-result-prompt">无数据展示...</div>
			</Card>
		</span>
		<span v-show="t_edit" ref="edit">
			<!-- v-bind. see https://vuejs.org/v2/api/#v-bind -->
			<EditBox
				v-if="actEdit"
				:addinName="name"
				:widgetAnnotation="widgetAnnotation"
				:editExtendInfo="editExtendInfo"
				ref="editbox"
				v-model="args"
				:attributes="filter_attributes"
				:router="router"
				:route="route"
				:root="root"
				:itemValue="itemValue"
				:query_oprs="query_oprs"
				:dataTypes="dataTypes"
				:targetclass="itemValue.data.targetClass"
			>
				<div slot="attribute">
					<!-- 从外部引用的辅助标签,用于显示属性 -->
					<p>标题属性(Title)</p>
					<AttributeSelector
						:target-class="args.bindTargetClass"
						v-model="args.attributeForTitle"
					></AttributeSelector>
					<p>详情属性(Label)</p>
					<AttributeSelector
						:target-class="args.bindTargetClass"
						v-model="args.attributeForLabel"
					></AttributeSelector>
					<p>图标属性(Icon)</p>
					<AttributeSelector
						:target-class="args.bindTargetClass"
						v-model="args.attributeForIcon"
					></AttributeSelector>
					<Button type="primary" long @click="freshData">刷新数据</Button>
				</div>
			</EditBox>
		</span>
	</section>
	<section v-else :addinName="name">
		<span style="text-align: center">
			<div class="form-addin-icon">
				<!-- 控件拖放区图标的样式
				see: http://ise.thss.tsinghua.edu.cn/font_857540_a2fo0rqai0f/demo_index.html-->
				<i class="iconfont"></i>
			</div>
			<!-- 在控件拖放区图标的名字 -->
			<div class="form-addin-name">简单列表</div>
		</span>
	</section>
</template>

<script>
// 从本地引擎的一个自定义标签
import AttributeSelector from "./AttributeSelector";
import EditBox from "@/ext_components/form/_EditBox.vue";
import { mapGetters, mapActions } from "vuex";

export default {
	props: [
		"widgetAnnotationaddin",
		"itemValuebasicArgs",
		"attributesargsProps",
		"routeactiveUUID",
		"routerstore",
		"rootitemValue",
		"query_oprsattributes",
		"relation",
		"editExtendInfo",
		"widgetAnnotation",
		"checkResult",
		"query_oprs",
		"route",
		"router",
		"root",
		"Message",
		"echarts",
	],
	components: {
		EditBox,
		AttributeSelector,
	},
	// Vue数据绑定的时候要求返回的结果
	data() {
		return {
			// 插件的名字
			name: "simpleCellGroup",
			// 表示是否已经进入画布区
			t_preview: false,
			// 是否显示控件的属性编辑区
			t_edit: false,
			// 属性配置项,按需设置
			args: {
				// 直接复用DWF默认的配置项,从而无需自行开发配置界面
				bindTargetClass: "",
				// 标签作为列表标题
				label: "",
				// 建模设置的过滤条件
				filterQuery: "",
				// 高度取值和单位
				height: "",
				heightType: "px",
				// 宽度取值和单位
				width: "",
				widthType: "px",
				// 背景色设置
				back_color: "",
				// 文字颜色设置
				txt_fontColor: "",
				/* ---------------------
				 * 下面是新增的自定义属性
				 * -------------------- */
				// 单元格显示标题
				attributeForTitle: "",
				// 单元格显示内容
				attributeForLabel: "",
				// 显示图标对应属性
				attributeForIcon: "",
				// 在配置区支持的事件列表,元素为事件中文名
				eventRange: ["单击"],
				// 已被用户设置的事件列表,元素格式为 { opr_type: '', opr_path: '', name: '事件中文名' }
				events: [],
			},
			cellData: {},
		};
	},
	mounted() {
		// 生命周期函数,实现数据加载
		this.freshData();
	},
	computed: {
		//...mapGetters("DWF_form", ["Entities", "Relations"]),
		cardStyle() {
			// 外层卡片的样式
			const result = {};
			if (this.args.height) {
				result["height"] =
					String(this.args.height) + (this.args.heightType || "px");
			}
			if (this.args.width) {
				result["width"] =
					String(this.args.width) + (this.args.widthType || "px");
			}
			if (this.args.back_color) {
				result["backgroundColor"] = this.args.back_color;
			}
			if (this.args.txt_fontColor) {
				result["color"] = this.args.txt_fontColor;
			}
			return result;
		},
	},
	methods: {
		// use `handleQueryData` to fetch data.
		...mapActions("DWF_form", ["handleQueryData"]),
		/*
		在建模的时候传入,提示控件是在画布区还是在控件列表中
		0:表示在画布区,已经被拖入
		1:表示在控件区,准备被拖入
		2: 表示在拖动中,还未放下
		*/
		setDisplayType(type) {
			if (type == 0) this.t_preview =
true;
			return this;
		},
		// 默认不用变化,返回编辑框供建模工具绘制,当控件拖入画布区以后被点击的时候触发
		getEditBox() {
			this.t_edit = true;
			return this.$refs.edit;
		},
		// 当插件无法直接通过style设置高度时,使用setHeight方法设置高度
		setHeight() {
			if (!this.$refs.main) return;
		},
		// 现有表单加载在画布区加载控件的时候,会将之前的配置传入
		setArgs(args) {
			for (var i in args) {
				this.args[i] = args[i];
			}
			return this;
			},
		// 表单保存的时候将控件的设置合并到表单自身的JSON中
		getArgs() {
			return this.args;
		},
		// 是否允许其他子控件拖入,如果不允许则返回null表单保存的时候将控件的设置合并到表单自身的JSON中
		getAllowgetArgs() {
			return nullthis.args;
		},
		// 返回控件绑定的目标属性,如果没有绑定返回undefined或者null
		getFormName() {
			return null;
		},
		freshData() {
			const bindTargetClass = this.args.bindTargetClass;
			if (!bindTargetClass) {
				console.error("no bindTargetClass");
				return;
			}

			const regexForTargetClass = /^([0-9a-zA-Z_]+)&([er])$/;
			const result = regexForTargetClass.exec(bindTargetClass);
			if (result === null) {
				console.error(
					`handleRefresh: invalid targetClass value -> ${bindTargetClass}`
				);
				return;
			}

			let targetClass = result[1];
			let query = {
				query: this.args.filterQuery,
				pageSize: 4,
				startIndex: 0,
			};

			let objs = this.handleQueryData({
				targetClass: targetClass,
				query: query,
				fresh: true,
			}).then((res) => {
				let objs = res;
				this.cellData = {
					title: this.args.label,
					icon: "ios-bookmarks-outline",
					list: res,
				};
			});
		},
	},
};
</script>

<style scoped>
/* 没有合适的数据的时候使用的样式 */
.no-result-prompt {
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: #eeeeee;
	color: #00000022;
	font-weight: bold;
	font-size: 2em;
}

/* hack to respond to height change.
     see https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
   */
.cell-group-wrapper >>> .ivu-card-body {
	position: relative;
	height: calc(100% - 60px);
}
.cell-group-wrapper >>> .ivu-cell-group {
	height: 100%;
	overflow-y: auto;
}
</style>

...

代码块
languagejs
titlepart-web/modeler/forms/simpleCellGroup.vue
collapsetrue
<template>
	<!--
    建模时的预览前端,即插件的实际显示样式
    :addinName="name"和ref="main"一般情况不可去除
	-->
	<section :addinName="name" ref="main">
		<!-- 在画布区的显示内容 -->
		<span>
			<!-- see https://www.iviewui.com/components/cell#JCYF -->
			<Card :style="cardStyle" class="cell-group-wrapper">
				<p slot="title">
					<!-- change to slot syntax to avoid a bug, where :title may not respond to change as a prop. -->
					<Icon :type="cellData.icon"></Icon>
					{{ cellData.title }}
				</p>
				<CellGroup
					v-if="cellData.list && cellData.list.length"
					@on-click="onClick"
				>
					<Cell
						v-for="(obj, idx) in cellData.list"
						:key="obj.oid"
						:name="idx"
						:title="String(obj[args.attributeForTitle])"
						:label="String(obj[args.attributeForLabel])"
						@click="onClick"
					>
						<Avatar :src="String(obj[args.attributeForIcon])" slot="icon" />
					</Cell>
				</CellGroup>
				<div v-else class="no-result-prompt">无数据展示...</div>
			</Card>
		</span>
	</section>
</template>

<script>
// 引入内部图标备用
import "@/styles/component/iconfont.css";
import { mapActions } from "vuex";

export default {
	props: [
		"widgetAnnotation",
		"itemValue",
		"attributes",
		"route",
		"router",
		"root",
		"store",
		"query_oprs",
	],
	// Vue数据绑定的时候要求返回的结果
	data() {
		return {
			// 插件的名字
			name: "simpleCellGroup",
			// 表示是否已经进入画布区
			t_preview: false,
			// 是否显示控件的属性编辑区
			t_edit: false,
			// 属性配置项,按需设置
			args: {
				// 直接复用DWF默认的配置项,从而无需自行开发配置界面
				bindTargetClass: "",
				// 标签作为列表标题
				label: "",
				// 建模设置的过滤条件
				filterQuery: "",
				// 高度取值和单位
				height: "",
				heightType: "px",
				// 宽度取值和单位
				width: "",
				widthType: "px",
				// 背景色设置
				back_color: "",
				// 文字颜色设置
				txt_fontColor: "",

				/* ---------------------
				 * 下面是新增的自定义属性
				 * -------------------- */
				// 单元格显示标题
				attributeForTitle: "",
				// 单元格显示内容
				attributeForLabel: "",
				// 已被用户设置的事件列表,元素格式为 { opr_type: '', opr_path: '', name: '事件中文名' }
				events: [],
			},
			// 需要显示数据的集合,包括标题,图标,单元标题,单元内容,约定格式如下
			cellData: {},
			// 记录被点击选择的对象
			cellSelected: {},
		};
	},
	mounted() {
		// 生命周期函数,实现数据加载
		this.freshData();
	},
	computed: {
		cardStyle() {
			// 外层卡片的样式
			const result = {};
			if (this.args.height) {
				result["height"] =
					String(this.args.height) + (this.args.heightType || "px");
			}
			if (this.args.width) {
				result["width"] =
					String(this.args.width) + (this.args.widthType || "px");
			}
			if (this.args.back_color) {
				result["backgroundColor"] = this.args.back_color;
			}
			if (this.args.txt_fontColor) {
				result["color"] = this.args.txt_fontColor;
			}
			return result;
		},
	},
	methods: {
		// use `handleQueryData` to fetch data.
		...mapActions("DWF_form", ["handleQueryData"]),
		/*
		在建模的时候传入,提示控件是在画布区还是在控件列表中
		0:表示在画布区,已经被拖入
		1:表示在控件区,准备被拖入
		2: 表示在拖动中,还未放下
		*/
		setDisplayType(type) {
			if (type == 0) this.t_preview = true;
			return this;
		},
		// 默认不用变化,返回编辑框供建模工具绘制,当控件拖入画布区以后被点击的时候触发 		getEditBox() {
			this.t_edit = true;
			return this.$refs.edit;
		},
		// 当插件无法直接通过style设置高度时,使用setHeight方法设置高度
		setHeight() {
			if (!this.$refs.main) return;
		},
		// 现有表单加载在画布区加载控件的时候,会将之前的配置传入
		setArgs(args) {
			for (var i in args) {
				this.args[i] = args[i];
			}
			return this;
		},
		// 表单保存的时候将控件的设置合并到表单自身的JSON中
		getArgs() {
			return this.args;
		},
		// 是否允许其他子控件拖入,如果不允许则返回null
		getAllow() {
			return null;
		},
		// 返回控件绑定的目标属性,如果没有绑定返回undefined或者null
		getFormName() {
			return null;
		},
		getSelected() {
			return this.cellSelected;
		},
		getAll() {
			return this.cellData.list;
		},
		// 单击事件
		onClick(idx) {
			this.cellSelected = this.cellData.list[idx];
			this.triggerEvent("单击");
		},
		// 根据名字触发表单事件上配置的操作
		triggerEvent(eventName) {
			let eventConfig = null;
			// 提取事件对应的操作参数
			if (this.args.events && this.args.events.length > 0) {
				eventConfig = this.args.events.find((val) => {
					return val.name === eventName;
				});
			}
			// 触发实际操作
			if (eventConfig) {
				this.invokeOperation(
					eventConfig.opr_type,
					eventConfig.opr_path,
					this.itemValue,
					this.store
				);
			}
		},
		// 刷新控件内部数据
		freshData() {
			const bindTargetClass = this.args.bindTargetClass;

			if (!bindTargetClass) {
				console.error("no bindTargetClass");
				return;
			}

			const regexForTargetClass = /^([0-9a-zA-Z_]+)&([er])$/;
			const glob = regexForTargetClass.exec(bindTargetClass);
			if (glob === null) {
				console.error(
					`handleRefresh: invalid targetClass value -> ${bindTargetClass}`
				);
				return;
			}

			const targetClass = glob[1];
			const query = this.args.filterQuery;

			console.log(this.dwf_ctx);

			this.handleQueryData({
				targetClass: targetClass,
				query: query,
				fresh: true,
			}).then((res) => {
				const attributeForTitle = this.args.attributeForTitle;
				const attributeForLabel = this.args.attributeForLabel;
				let objs = res;
				this.cellData = {
					title: this.args.label,
					icon: "ios-bookmarks-outline",
					list: res,
				};
			});
		},
	},
};
</script>

<style scoped>
/* 没有合适的数据的时候使用的样式 */
.no-result-prompt {
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: #eeeeee;
	color: #00000022;
	font-weight: bold;
	font-size: 2em;
}

/* hack to respond to height change.
     see https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
   */
.cell-group-wrapper >>> .ivu-card-body {
	position: relative;
	height: calc(100% - 60px);
}
.cell-group-wrapper >>> .ivu-cell-group {
	height: 100%;
	overflow-y: auto;
}
</style>

...