提交 10ad4d5c 编写于 作者: Mosher's avatar Mosher

add:新增实体数据视图、卡片视图部件支持

上级 459e1d7c
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
{{#eq ctrl.controlType "APPMENU"}}{{> @macro/front-end/views/view-control/view-menu/view-control-menu.hbs}}{{/eq~}} {{#eq ctrl.controlType "APPMENU"}}{{> @macro/front-end/views/view-control/view-menu/view-control-menu.hbs}}{{/eq~}}
{{#eq ctrl.controlType "PICKUPVIEWPANEL"}}{{> @macro/front-end/views/view-control/view-pickup-panel/view-pickup-panel.hbs}}{{/eq~}} {{#eq ctrl.controlType "PICKUPVIEWPANEL"}}{{> @macro/front-end/views/view-control/view-pickup-panel/view-pickup-panel.hbs}}{{/eq~}}
{{#eq ctrl.controlType "TREEVIEW"}}{{> @macro/front-end/views/view-control/view-tree/view-control-tree.hbs}}{{/eq~}} {{#eq ctrl.controlType "TREEVIEW"}}{{> @macro/front-end/views/view-control/view-tree/view-control-tree.hbs}}{{/eq~}}
{{#eq ctrl.controlType "PANEL"}}{{> @macro/front-end/views/view-control/view-panel/view-control-panel.hbs}}{{/eq~}} {{#eq ctrl.controlType "PANEL"}}{{> @macro/front-end/views/view-control/view-panel/view-control-panel.hbs}}{{/eq~}}
\ No newline at end of file {{#eq ctrl.controlType "DATAVIEW"}}{{> @macro/front-end/views/view-control/view-dataview/view-control-dataview.hbs}}{{/eq~}}
\ No newline at end of file
{{ctrl.name}}:{
action:{
loadAction: '{{ctrl.getPSControlAction.psAppDEMethod.codeName}}',
removeAction: '{{ctrl.removePSControlAction.psAppDEMethod.codeName}}',
updateAction: '{{ctrl.updatePSControlAction.psAppDEMethod.codeName}}',
loadDraftAction: '{{ctrl.getDraftPSControlAction.psAppDEMethod.codeName}}',
createAction: '{{ctrl.createPSControlAction.psAppDEMethod.codeName}}',
fetchAction:'{{ctrl.fetchPSControlAction.psAppDEMethod.codeName}}'
}
},
\ No newline at end of file
<script setup lang="ts"> <script setup lang="ts">
import { IActionParam, IParam } from '@core'; import { hasKey, IParam } from '@core';
interface MenuCenterProps { interface MenuCenterProps {
menus: IParam[]; menus: IParam[];
counterData?: IParam; counterData?: IParam;
...@@ -33,7 +33,7 @@ const getLayout = (item: IParam ,name: string) => { ...@@ -33,7 +33,7 @@ const getLayout = (item: IParam ,name: string) => {
} }
const hasCounter = (item: IParam): boolean => { const hasCounter = (item: IParam): boolean => {
if (item.counterId && props.counterData && props.counterData.hasOwnProperty(item.counterId)) { if (item.counterId && props.counterData && hasKey(props.counterData, item.counterId)) {
return true; return true;
} }
return false; return false;
......
<script setup lang="ts"> <script setup lang="ts">
import { IParam } from "@core"; import { IParam, hasKey } from "@core";
interface Props{ interface Props{
items: IParam[], items: IParam[],
collapsed?: boolean, collapsed?: boolean,
...@@ -10,7 +10,7 @@ const props = withDefaults(defineProps<Props>(), { ...@@ -10,7 +10,7 @@ const props = withDefaults(defineProps<Props>(), {
}); });
const hasCounter = (item: IParam): boolean => { const hasCounter = (item: IParam): boolean => {
if (item.counterId && props.counterData && props.counterData.hasOwnProperty(item.counterId)) { if (item.counterId && props.counterData && hasKey(props.counterData, item.counterId)) {
return true; return true;
} }
return false; return false;
......
<script setup lang="ts"> <script setup lang="ts">
import { IParam, IActionParam } from "@core"; import { IParam, IActionParam, hasKey } from "@core";
import { onBeforeMount, ref, Ref } from "vue"; import { ref, Ref } from "vue";
interface ToolbarProps { interface ToolbarProps {
...@@ -39,7 +39,7 @@ const itemClick = (item: IParam) => { ...@@ -39,7 +39,7 @@ const itemClick = (item: IParam) => {
}; };
const hasCounter = (item: any) => { const hasCounter = (item: any) => {
if (item.counterId && props.counterData && props.counterData.hasOwnProperty(item.counterId)) { if (item.counterId && props.counterData && hasKey(props.counterData, item.counterId)) {
return true; return true;
} }
return false; return false;
......
<script setup lang="ts">
</script>
<template>
<AppViewBaseLayout class="app-data-view-layout">
<template v-slot:header-top>
<slot name="topMessage" />
</template>
<template v-slot:header-left>
<slot name="caption" />
</template>
<template v-slot:header-content>
</template>
<template v-slot:header-right>
<div class="data-view__header-right">
<slot name="quickGroupSearch" />
<slot name="quickSearch" />
<slot name="toolbar" />
</div>
</template>
<template v-slot:header-bottom>
<div>
<slot name="quickSearchForm" />
<slot name="searchForm" />
<slot name="searchBar" />
</div>
</template>
<template v-slot:body-top>
<slot name="bodyMessage" />
</template>
<slot />
<template v-slot:footer-content>
<slot name="bottomMessage" />
</template>
</AppViewBaseLayout>
</template>
<style lang="scss">
.app-data-view-layout {
.data-view__header-right {
display: flex;
flex-wrap: nowrap;
}
}
</style>
\ No newline at end of file
import { MDViewProps } from "../md-view";
/**
* 实体数据视图输入参数
*
* @export
* @interface DataViewProps
* @extends {MDViewProps}
*/
export interface DataViewProps extends MDViewProps {}
\ No newline at end of file
import { MDViewState } from "../md-view";
/**
* 实体数据视图状态
*
* @export
* @interface DataViewState
* @extends {MDViewState}
*/
export interface DataViewState extends MDViewState { }
\ No newline at end of file
import { IParam } from "@core";
import { MDView } from "../md-view";
import { DataViewProps } from "./data-view-prop";
import { DataViewState } from "./data-view-state";
/**
* 实体数据视图
*
* @export
* @class DataView
* @extends {MDView}
*/
export class DataView extends MDView {
/**
* 实体数据视图状态
*
* @type {DataViewState}
* @memberof DataView
*/
public declare state: DataViewState;
/**
* 实体数据视图输入参数
*
* @type {DataViewProps}
* @memberof DataView
*/
public declare props: DataViewProps;
/**
* @description 列表部件
* @type {IParam}
* @memberof DataView
*/
public declare dataViewControl: IParam;
/**
* @description 处理视图初始化
* @memberof DataView
*/
public useViewInit(): void {
super.useViewInit();
this.dataViewControl = ref(null);
}
/**
* @description 获取多数据部件
* @return {*}
* @memberof DataView
*/
public getMDCtrl() {
return unref(this.dataViewControl);
}
/**
* @description 安装视图所有功能模块的方法
* @return {*}
* @memberof DataView
*/
public moduleInstall() {
const superParams = super.moduleInstall();
return {
...superParams,
dataViewControl: this.dataViewControl
};
}
}
\ No newline at end of file
export * from './data-view-prop'
export * from './data-view-state'
export * from './data-view'
\ No newline at end of file
...@@ -16,4 +16,5 @@ export * from './list-view' ...@@ -16,4 +16,5 @@ export * from './list-view'
export * from './panel-view' export * from './panel-view'
export * from './pickup-tree-view' export * from './pickup-tree-view'
export * from './list-exp-view' export * from './list-exp-view'
export * from './grid-exp-view' export * from './grid-exp-view'
\ No newline at end of file export * from './data-view'
\ No newline at end of file
import { IParam } from "@core"; import { IParam, ListViewProps, ListViewState } from "@core";
import { MDView } from "../md-view"; import { MDView } from "../md-view";
/** /**
...@@ -9,6 +9,22 @@ import { MDView } from "../md-view"; ...@@ -9,6 +9,22 @@ import { MDView } from "../md-view";
*/ */
export class ListView extends MDView { export class ListView extends MDView {
/**
* 实体列表视图状态
*
* @type {ListViewState}
* @memberof ListView
*/
public declare state: ListViewState;
/**
* 实体列表视图输入参数
*
* @type {ListViewProps}
* @memberof ListView
*/
public declare props: ListViewProps;
/** /**
* @description 列表部件 * @description 列表部件
* @type {IParam} * @type {IParam}
...@@ -37,7 +53,7 @@ export class ListView extends MDView { ...@@ -37,7 +53,7 @@ export class ListView extends MDView {
/** /**
* @description 安装视图所有功能模块的方法 * @description 安装视图所有功能模块的方法
* @return {*} * @return {*}
* @memberof GridView * @memberof ListView
*/ */
public moduleInstall() { public moduleInstall() {
const superParams = super.moduleInstall(); const superParams = super.moduleInstall();
......
import { ViewBase, MainViewState, IActionParam, IParam, isExistAndNotEmpty, hasFunction } from '@core'; import { ViewBase, MainViewState, IActionParam, IParam, isExistAndNotEmpty, hasFunction, hasKey } from '@core';
/** /**
* 实体视图 * 实体视图
...@@ -96,7 +96,7 @@ export class MainView extends ViewBase { ...@@ -96,7 +96,7 @@ export class MainView extends ViewBase {
} }
for (const key in this.state.toolbar) { for (const key in this.state.toolbar) {
// 设置初始化状态 // 设置初始化状态
if (!this.state.toolbar.hasOwnProperty(key)) { if (!hasKey(this.state.toolbar, key)) {
return; return;
} }
const item = this.state.toolbar[key]; const item = this.state.toolbar[key];
......
...@@ -320,9 +320,7 @@ export class MDView extends MainView { ...@@ -320,9 +320,7 @@ export class MDView extends MainView {
* @memberof MDView * @memberof MDView
*/ */
public onQuickSearchEvent($event: any) { public onQuickSearchEvent($event: any) {
if ($event) { this.handleQuickSearch($event);
this.handleQuickSearch($event);
}
} }
/** /**
......
import { MDControlProps } from "../md-control";
/**
* 实体卡片视图部件输入参数
*
* @export
* @interface DataViewControlProps
* @extends {MDControlProps}
*/
export interface DataViewControlProps extends MDControlProps {}
\ No newline at end of file
import { MDControlState } from "../md-control";
/**
* 实体卡片视图部件状态
*
* @export
* @interface DataViewControlState
* @extends {MDControlState}
*/
export interface DataViewControlState extends MDControlState {}
\ No newline at end of file
import { IParam } from "@core";
import { IActionParam } from "@core/interface";
import { Ref } from "vue";
import { ControlStateBase } from "../control-base";
import { MDControl } from "../md-control";
import { DataViewControlProps } from "./dataview-control-prop";
import { DataViewControlState } from "./dataview-control-state";
/**
* 实体卡片视图部件
*
* @export
* @class DataViewControl
* @extends {MDControl}
*/
export class DataViewControl extends MDControl {
/**
* 实体卡片视图部件状态
*
* @type {DataViewControlState}
* @memberof DataViewControl
*/
public declare state: DataViewControlState;
/**
* 实体卡片视图部件输入参数
*
* @type {DataViewControlProps}
* @memberof DataViewControl
*/
public declare props: DataViewControlProps;
/**
* @description 处理状态变化
* @memberof DataViewControl
*/
public handleStateChange() {
// 计算列表数据界面行为权限
const { items, uIActions } = toRefs(this.state);
if (items.value.length > 0 && uIActions.value && Object.keys(uIActions.value).length > 0) {
Object.keys(uIActions.value).forEach((key: string) => {
const tempActionModel = uIActions.value[key];
items.value.forEach((item: any) => {
Object.assign(item, { [key]: this.getActionAuthState(item, [tempActionModel]) });
});
})
}
// 清空选中数据,避免多次加载选中效果异常
this.state.selections = [];
// 处理分组
this.handleDataGroup();
// 处理默认选中
this.handleDefaultSelect();
}
/**
* @description 默认选中
* @private
* @memberof DataViewControl
*/
private handleDefaultSelect() {
const { selectFirstDefault, controlName } = this.state;
const { selections, items } = toRefs(this.state);
if (selectFirstDefault) {
if (items.value && items.value.length > 0) {
selections.value.push(items.value[0]);
this.emit("ctrlEvent", {
tag: controlName,
action: "selectionChange",
data: [items.value[0]],
});
}
}
}
/**
* @description 触发界面行为
* @protected
* @param {IParam} item 列表数据
* @param {IParam} action 界面行为
* @param {MouseEvent} event 鼠标源事件
* @memberof DataViewControl
*/
protected onUIAction(item: IParam, action: IParam, event: MouseEvent) {
if (!item || !action) {
console.warn("行为执行参数不足");
return;
}
const inputParam = {
context: this.state.context,
viewParams: this.state.viewParams,
data: [item],
event: event,
actionEnvironment: this
};
// 执行行为
App.getAppActionService().execute(action, inputParam);
}
/**
* 使用自定义模块
*
* @protected
* @return {*}
* @memberof DataViewControl
*/
protected useCustom() {
const { controlName } = this.state;
/**
* 数据项选中
*
* @param {IParam} item 数据项
* @param {MouseEvent} event 鼠标源事件
*/
const onDataViewItemSelected = (item: IParam, event: MouseEvent) => {
const { isMultiple } = this.state;
const { selections } = toRefs(this.state);
const index = selections.value.findIndex((selection: any) => selection.srfkey === item.srfkey);
// 存在选中则删除
if (index !== -1) {
selections.value.splice(index, 1);
} else {
// 单选清空已选中
if (!isMultiple) {
selections.value.splice(index, selections.value.length);
}
selections.value.push(item);
}
this.emit('ctrlEvent', { tag: controlName, action: 'selectionChange', data: selections.value });
}
/**
* 是否选中
*
* @param item 列表项
* @returns
*/
const isSelected = (item: IParam): boolean => {
const { selections } = toRefs(this.state);
return selections.value.findIndex((selection: any) => selection.srfkey === item.srfkey) !== -1;
}
return {
onDataViewItemSelected: onDataViewItemSelected.bind(this),
isSelected: isSelected.bind(this)
}
}
/**
* 安装部件功能模块
*
* @return {*}
* @memberof DataViewControl
*/
public moduleInstall() {
const superParams = super.moduleInstall();
return {
...superParams,
onUIAction: this.onUIAction.bind(this),
useCustom: this.useCustom.bind(this)
}
}
}
\ No newline at end of file
export * from './dataview-control-prop'
export * from './dataview-control-state'
export * from './dataview-control'
\ No newline at end of file
import { DataTypes, dateFormat, deepCopy, GridControlState, IActionParam, IParam, isExistAndNotEmpty, MDControl } from "@core"; import { DataTypes, dateFormat, deepCopy, GridControlState, hasKey, IActionParam, IParam, isExistAndNotEmpty, MDControl } from "@core";
import { createUUID } from "qx-util"; import { createUUID } from "qx-util";
import schema, { ErrorList, FieldErrorList } from 'async-validator'; import schema, { ErrorList, FieldErrorList } from 'async-validator';
import { Ref } from "vue"; import { Ref } from "vue";
...@@ -271,7 +271,7 @@ export class GridControl extends MDControl { ...@@ -271,7 +271,7 @@ export class GridControl extends MDControl {
); );
if (response.status && response.status == 200) { if (response.status && response.status == 200) {
updateItem.updateDetails?.forEach((detailName: string) => { updateItem.updateDetails?.forEach((detailName: string) => {
if (detailName && items[rowIndex].hasOwnProperty(detailName)) { if (detailName && hasKey(items[rowIndex], detailName)) {
items[rowIndex][detailName] = response.data[detailName]; items[rowIndex][detailName] = response.data[detailName];
} }
}); });
...@@ -647,7 +647,7 @@ export class GridControl extends MDControl { ...@@ -647,7 +647,7 @@ export class GridControl extends MDControl {
} }
break; break;
} }
} else if (createDV && row.hasKey(property)) { } else if (createDV && hasKey(row, property)) {
row[property] = dataType && DataTypes.isNumber(dataType) ? Number(createDV) : createDV; row[property] = dataType && DataTypes.isNumber(dataType) ? Number(createDV) : createDV;
} }
}); });
...@@ -696,7 +696,7 @@ export class GridControl extends MDControl { ...@@ -696,7 +696,7 @@ export class GridControl extends MDControl {
} }
break; break;
} }
} else if (updateDV && row.hasKey(property)) { } else if (updateDV && hasKey(row, property)) {
row[property] = dataType && DataTypes.isNumber(dataType) ? Number(updateDV) : updateDV; row[property] = dataType && DataTypes.isNumber(dataType) ? Number(updateDV) : updateDV;
} }
}); });
......
...@@ -18,4 +18,5 @@ export * from './tab-view-panel-control' ...@@ -18,4 +18,5 @@ export * from './tab-view-panel-control'
export * from './list-control' export * from './list-control'
export * from './panel-control' export * from './panel-control'
export * from './list-exp-bar-control' export * from './list-exp-bar-control'
export * from './grid-exp-bar-control' export * from './grid-exp-bar-control'
\ No newline at end of file export * from './dataview-control'
\ No newline at end of file
import qs from "qs"; import qs from "qs";
import { clearCookie, getCookie, setCookie } from "qx-util"; import { clearCookie, getCookie, setCookie } from "qx-util";
import { deepCopy, getSessionStorage, Http, IAppAuthService, IParam, removeSessionStorage, setSessionStorage } from "@core"; import { deepCopy, getSessionStorage, Http, IAppAuthService, IParam, removeSessionStorage, setSessionStorage } from "@core";
import { hasKey } from "@core/utils";
/** /**
* 应用权限服务基类 * 应用权限服务基类
...@@ -161,7 +162,7 @@ export abstract class AppAuthServiceBase implements IAppAuthService { ...@@ -161,7 +162,7 @@ export abstract class AppAuthServiceBase implements IAppAuthService {
result[dataAccAction] = 0; result[dataAccAction] = 0;
} }
// 主状态权限 // 主状态权限
if (mainSateOPPrivs && (Object.keys(mainSateOPPrivs).length > 0) && mainSateOPPrivs.hasOwnProperty(dataAccAction)) { if (mainSateOPPrivs && (Object.keys(mainSateOPPrivs).length > 0) && hasKey(mainSateOPPrivs, dataAccAction)) {
result[dataAccAction] = result[dataAccAction] && mainSateOPPrivs[dataAccAction]; result[dataAccAction] = result[dataAccAction] && mainSateOPPrivs[dataAccAction];
} }
// 实体级权限 // 实体级权限
......
import { ControlServiceBase, ControlVOBase, hasFunction, IParam } from '@core';
/**
* @description 表格部件服务
* @export
* @class DataViewService
* @extends {ControlServiceBase<T>}
* @template T 部件数据对象类型
*/
export class DataViewService<T extends ControlVOBase> extends ControlServiceBase<T> {
/**
* @description 加载草稿
* @param {IParam} context 上下文
* @param {IParam} data 行为数据
* @param { action: string; isLoading?: boolean } opts 行为参数
* @return {*} {Promise<any>}
* @memberof DataViewService
*/
public async loadDraft(context: IParam, data: IParam, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
// todo主键
const action = hasFunction(_entityService, opts.action) ? opts.action : 'GetDraft';
const response = await _entityService[action](Context, Data, opts.isLoading);
response.data = this.newControlVO(response.data);
response.data.srfuf = '0';
return this.handleResponse(response, opts);
}
/**
* @description 加载数据
* @param {IParam} context 上下文
* @param {IParam} data 行为数据
* @param { action: string; isLoading?: boolean } opts 行为参数
* @return {*} {Promise<any>}
* @memberof DataViewService
*/
public async search(context: IParam, data: IParam, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
const action = hasFunction(_entityService, opts.action) ? opts.action : 'FetchDefault';
const response = await _entityService[action](Context, Data, opts.isLoading);
let resData:any[] = response.data;
for (let index = 0; index < resData.length; index++) {
resData[index] = this.newControlVO(resData[index]);
}
return this.handleResponse(response, opts);
}
/**
* @description 新建数据
* @param {IParam} context 上下文
* @param {IParam} data 行为数据
* @param { action: string; isLoading?: boolean } opts 行为参数
* @return {*} {Promise<any>}
* @memberof DataViewService
*/
public async create(context: IParam, data: IParam, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
const action = hasFunction(_entityService, opts.action) ? opts.action : 'Create';
const response = await _entityService[action](Context, Data, opts.isLoading);
response.data = this.newControlVO(response.data);
return this.handleResponse(response, opts);
}
/**
* @description 删除数据
* @param {IParam} context 上下文
* @param {IParam} data 行为数据
* @param { action: string; isLoading?: boolean } opts 行为参数
* @return {*} {Promise<any>}
* @memberof DataViewService
*/
public async remove(context: IParam, data: IParam, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
const action = hasFunction(_entityService, opts.action) ? opts.action : 'Remove';
const response = await _entityService[action](Context, Data, opts.isLoading);
response.data = this.newControlVO(response.data);
return this.handleResponse(response, opts);
}
/**
* @description 更新
* @param {*} context 上下文
* @param {*} data 行为数据
* @param { action: string; isLoading?: boolean } opts 行为参数
* @return {*} {Promise<any>}
* @memberof DataViewService
*/
public async update(context: any, data: any, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
const action = hasFunction(_entityService, opts.action) ? opts.action : 'Update';
const response = await _entityService[action](Context, Data, opts.isLoading);
response.data = this.newControlVO(response.data);
return this.handleResponse(response, opts);
}
/**
* 前台逻辑
*
* @param [context={}] 上下文参数
* @param [data={}] 视图参数
* @param opts
* @return {*}
*/
public async frontLogic(context: any, data: any, opts: { action: string; isLoading?: boolean }): Promise<any> {
let _entityService: any = this.entityService;
const { context: Context, data: Data } = this.handleRequestData(context, data, opts);
if (hasFunction(_entityService, opts.action)) {
const response = await _entityService[opts.action](Context, Data, opts.isLoading);
response.data = this.newControlVO(response.data);
return this.handleResponse(response, opts);
} else {
return null;
}
}
}
...@@ -2,4 +2,5 @@ export * from './control-service-base' ...@@ -2,4 +2,5 @@ export * from './control-service-base'
export * from './edit-form-service' export * from './edit-form-service'
export * from './grid-service' export * from './grid-service'
export * from './tree-service' export * from './tree-service'
export * from './list-service' export * from './list-service'
\ No newline at end of file export * from './dataview-service'
\ No newline at end of file
import { IParam } from "@core"; import { IParam } from "@core";
import { hasKey } from "../util";
/** /**
* 视图相关处理逻辑工具类 * 视图相关处理逻辑工具类
...@@ -19,7 +20,7 @@ export class UIUtil { ...@@ -19,7 +20,7 @@ export class UIUtil {
const result: any[] = []; const result: any[] = [];
if (!UIService) return; if (!UIService) return;
for (const key in actionModel) { for (const key in actionModel) {
if (!actionModel.hasOwnProperty(key)) { if (!hasKey(actionModel, key)) {
return result; return result;
} }
const item = actionModel[key]; const item = actionModel[key];
...@@ -78,7 +79,7 @@ export class UIUtil { ...@@ -78,7 +79,7 @@ export class UIUtil {
let value: string | null = params[name]; let value: string | null = params[name];
if (value && value.toString().startsWith('%') && value.toString().endsWith('%')) { if (value && value.toString().startsWith('%') && value.toString().endsWith('%')) {
const key = value.substring(1, value.length - 1).toLowerCase(); const key = value.substring(1, value.length - 1).toLowerCase();
if (activedata && (activedata.hasOwnProperty(key) || key in activedata)) { if (activedata && hasKey(activedata, key)) {
value = activedata[key]; value = activedata[key];
} else if (context && context[key]) { } else if (context && context[key]) {
value = context[key]; value = context[key];
......
import { hasKey } from "../util";
export class UIActionUtil { export class UIActionUtil {
...@@ -33,17 +34,11 @@ export class UIActionUtil { ...@@ -33,17 +34,11 @@ export class UIActionUtil {
let value: string | null = _params[name]; let value: string | null = _params[name];
if (value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%')) { if (value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%')) {
const key = value.substring(1, value.length - 1); const key = value.substring(1, value.length - 1);
if ( if (arg && hasKey(arg, key)) {
arg &&
arg.hasOwnProperty(key) &&
(Object.is(actionTarget, 'SINGLEKEY') ||
Object.is(actionTarget, 'SINGLEDATA') ||
Object.is(actionTarget, 'NONE'))
) {
value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null; value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null;
} else if (parentContext && parentContext.hasOwnProperty(key)) { } else if (parentContext && hasKey(parentContext, key)) {
value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null; value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null;
} else if (parentParams && parentParams.hasOwnProperty(key)) { } else if (parentParams && hasKey(parentParams, key)) {
value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null; value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null;
} else { } else {
hasProperty = false; hasProperty = false;
...@@ -64,11 +59,11 @@ export class UIActionUtil { ...@@ -64,11 +59,11 @@ export class UIActionUtil {
if (value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%')) { if (value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%')) {
const key = value.substring(1, value.length - 1); const key = value.substring(1, value.length - 1);
args.forEach((arg: any) => { args.forEach((arg: any) => {
if (arg && arg.hasOwnProperty(key)) { if (arg && hasKey(arg, key)) {
value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null; value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null;
} else if (parentContext && parentContext.hasOwnProperty(key)) { } else if (parentContext && hasKey(parentContext, key)) {
value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null; value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null;
} else if (parentParams && parentParams.hasOwnProperty(key)) { } else if (parentParams && hasKey(parentParams, key)) {
value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null; value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null;
} else { } else {
value = null; value = null;
......
...@@ -132,6 +132,18 @@ export function typeOf(obj: any): string { ...@@ -132,6 +132,18 @@ export function typeOf(obj: any): string {
return map[toString.call(obj)]; return map[toString.call(obj)];
} }
/**
* 对象是否有属性
* @description 适配使用get set的Object对象
* @export
* @param {*} obj 对象
* @param {string} key 属性标识
* @return {*} {boolean}
*/
export function hasKey(obj: any, key: string): boolean {
return obj.hasOwnProperty(key) || key in obj;
}
/** /**
* @description 校验值 * @description 校验值
* @param {*} value 值 * @param {*} value 值
......
...@@ -24,6 +24,7 @@ const routes = [ ...@@ -24,6 +24,7 @@ const routes = [
(eq appView.viewType 'DEGRIDEXPVIEW') (eq appView.viewType 'DEGRIDEXPVIEW')
(eq appView.viewType 'DELISTVIEW') (eq appView.viewType 'DELISTVIEW')
(eq appView.viewType 'DELISTEXPVIEW') (eq appView.viewType 'DELISTEXPVIEW')
(eq appView.viewType 'DEDATAVIEW')
(eq appView.viewType 'DEPANELVIEW') (eq appView.viewType 'DEPANELVIEW')
(eq appView.viewType 'DETREEEXPVIEW') (eq appView.viewType 'DETREEEXPVIEW')
(eq appView.viewType 'DETREEVIEW') (eq appView.viewType 'DETREEVIEW')
......
.app-dataview {
height: 100%;
.app-dataview-item:hover,
.app-dataview-item__selection {
box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, 0.08);
}
.app-dataview-item__action {
color: #2d8cf0;
cursor: pointer;
&:not(:first-child) {
padding-left: 12px;
}
i,
img {
padding-right: 6px;
}
}
.app-dataview-item__action.disabled {
color: #d2d2d2;
cursor: no-drop;
}
.not-data {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
justify-content: center;
}
}
\ No newline at end of file
...@@ -10,4 +10,5 @@ ...@@ -10,4 +10,5 @@
@use './app-list.scss'; @use './app-list.scss';
@use './app-panel.scss'; @use './app-panel.scss';
@use './app-pickup-view-panel.scss'; @use './app-pickup-view-panel.scss';
@use './app-exp-bar.scss'; @use './app-exp-bar.scss';
\ No newline at end of file @use './app-dataview.scss';
\ No newline at end of file
import {{page.codeName}} from "./{{spinalCase page.codeName}}.vue";
export default {{page.codeName}};
export const viewState = {
enableQuickSearch: {{#if page.enableQuickSearch}}{{page.enableQuickSearch}}{{else}}false{{/if}},
expandSearchForm: {{#if page.expandSearchForm}}{{page.expandSearchForm}}{{else}}false{{/if}},
{{> @macro/front-end/views/view-base-config.hbs}}
};
\ No newline at end of file
<script setup lang="ts">
import { Subject } from 'rxjs';
import { FilterOutlined } from '@ant-design/icons-vue';
import { DataView, IActionParam, IParam, IContext } from '@core';
import { viewState } from './{{spinalCase page.codeName}}-state';
{{#page.ctrls}}
{{#eq controlType "DATAVIEW"}}
import { {{codeName}}Dataview } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-dataview';
{{/eq}}
{{#if (and (eq controlType "SEARCHFORM") (eq name 'searchform'))}}
import { {{codeName}}SearchForm } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-search-form';
{{/if}}
{{#if (and (eq controlType "SEARCHFORM") (eq name 'quicksearchform'))}}
import { {{codeName}}QuickSearchForm } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-quick-search-form';
{{/if}}
{{#eq controlType "SEARCHBAR"}}
import { {{codeName}}SearchBar } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-searchBar';
{{/eq}}
{{/page.ctrls}}
// props声明和默认值处理
interface Props {
context?: IContext;
viewParams?: IParam;
openType?: "ROUTE" | "MODAL" | "EMBED";
viewSubject?: Subject<IActionParam>;
}
const props = withDefaults(defineProps<Props>(), {
openType:'ROUTE',
viewSubject: () => new Subject<IActionParam>()
})
// emit声明
interface ViewEmit {
(name: "viewEvent", value: IActionParam): void;
}
const emit = defineEmits<ViewEmit>();
// 安装功能模块,提供状态和能力方法
const dataView = new DataView(viewState, props, emit).moduleInstall();
const { state, dataViewControl, searchForm, quickSearchForm, onCtrlEvent, onToolbarEvent, onQuickGroupEvent, onQuickSearchEvent } = dataView;
{{#if page.psAppCounterRefs}}
// 计数器数据
const counterData = computed(() => {
const { counterServices } = toRefs(state);
if (counterServices && counterServices.value && counterServices.value.length) {
return counterServices.value[0].data;
}
return {};
})
{{/if}}
</script>
<template>
<AppDataViewLayout :class="['app-dataview-view', state.viewSysCss]" :showCaptionBar="state.showCaptionBar">
<template v-slot:caption>
<AppIconText class="app-view__caption" size="large" :text="state.viewCaption" />
</template>
{{#if page.enableQuickGroup}}
<template v-slot:quickGroupSearch>
<AppQuickGroup v-if="state.enableQuickGroup" :quickGroupModel="state.quickGroupPSCodeList" :context="state.context" :viewParams="state.viewParams" @onQuickGroupEvent="onQuickGroupEvent" />
</template>
{{/if}}
{{#page.ctrls}}
{{#eq controlType "TOOLBAR"}}
<template v-slot:toolbar>
<AppToolbar
mode="button"
name="{{lowerCase codeName}}"
:actionModel="state.toolbar"
{{#if page.psAppCounterRefs}}
:counterData="counterData"
{{/if}}
@onToolbarEvent="onToolbarEvent"/>
</template>
{{/eq}}
{{#if (and (eq controlType "SEARCHFORM") (eq name 'searchform'))}}
{{#if page.enableFilter}}
<template v-slot:quickSearch>
<div class='app-quick-search'>
<a-input-search v-if="state.enableQuickSearch" @search="onQuickSearchEvent" allowClear/>
<a-popover v-show="state.expandSearchForm" trigger="click" :overlayStyle="{width: '50%'}" placement="bottom">
<template #content>
<{{codeName}}SearchForm
ref="searchForm"
name="{{name}}"
:context="state.context"
:viewParams="state.viewParams"
:controlAction="state.{{camelCase name}}.action"
:viewSubject="state.viewSubject"
@ctrlEvent="onCtrlEvent"
></{{codeName}}SearchForm>
</template>
<a-button class="app-search-form__filter-button"><filter-outlined /></a-button>
</a-popover>
</div>
</template>
{{else}}
<template v-slot:searchForm>
<{{codeName}}SearchForm
v-show="state.expandSearchForm"
ref="searchForm"
name="{{name}}"
:context="state.context"
:viewParams="state.viewParams"
:controlAction="state.{{camelCase name}}.action"
:viewSubject="state.viewSubject"
@ctrlEvent="onCtrlEvent"
></{{codeName}}SearchForm>
</template>
{{/if}}
{{/if}}
{{#if (and (eq controlType "SEARCHFORM") (eq name 'quicksearchform'))}}
<template v-slot:quickSearchForm>
<{{codeName}}QuickSearchForm
ref="quickSearchForm"
name="{{name}}"
:context="state.context"
:viewParams="state.viewParams"
:controlAction="state.{{camelCase name}}.action"
:viewSubject="state.viewSubject"
@ctrlEvent="onCtrlEvent"
></{{codeName}}QuickSearchForm>
</template>
{{/if}}
{{#eq controlType "SEARCHBAR"}}
<template v-slot:searchBar>
<{{codeName}}SearchBar
:controlAction="state.{{camelCase name}}.action"
:viewSubject="state.viewSubject"/>
</template>
{{/eq}}
{{#eq controlType "DATAVIEW"}}
<{{codeName}}Dataview
ref="dataViewControl"
name="{{name}}"
:context="state.context"
:showBusyIndicator="true"
:viewParams="state.viewParams"
:controlAction="state.{{name}}.action"
:viewSubject="state.viewSubject"
:parent="dataView"
@ctrlEvent="onCtrlEvent"
></{{codeName}}Dataview>
{{/eq}}
{{/page.ctrls}}
</AppDataViewLayout>
</template>
<style lang="scss">
.app-dataview-view {
height: 100%;
width: 100%;
.app-search-form__filter-button {
transform: translateX(-2px);
}
}
</style>
\ No newline at end of file
import {{ctrl.codeName}}Dataview from "./{{spinalCase ctrl.codeName}}-dataview.vue";
export { {{ctrl.codeName}}Dataview };
import { DataViewService, ControlVOBase } from '@core';
import { {{pascalCase ctrl.psAppDataEntity.codeName}}Service } from '@api/{{spinalCase ctrl.psAppDataEntity.codeName}}/{{spinalCase ctrl.psAppDataEntity.codeName}}-service';
export class ControlVO extends ControlVOBase {
constructor(data: any) {
super(data);
// 数据视图部件自持属性
this.$ownKeys =
{{~#each ctrl.psDEDataViewDataItems as | dataItem | ~}}
{{#if @first}}[{{/if~}}
'{{lowerCase dataItem.name}}'{{#unless @last}},{{/unless}}
{{~#if @last}}];{{/if~}}
{{/each}}
}
{{#each ctrl.psDEDataViewDataItems as | dataItem |}}
{{#if dataItem.psAppDEField}}
get {{lowerCase dataItem.name}}() {
return this.$DO.{{lowerCase dataItem.psAppDEField.codeName}};
}
set {{lowerCase dataItem.name}}(value: any) {
this.$DO.{{lowerCase dataItem.psAppDEField.codeName}} = value;
}
{{/if}}
{{/each}}
}
export const ctrlState = {
controlCodeName: '{{ctrl.codeName}}',
controlName: '{{ctrl.name}}',
appEntityCodeName: '{{ctrl.psAppDataEntity.codeName}}',
appDeCodeName:'{{ctrl.psAppDataEntity.codeName}}',
appDeLogicName: '{{ctrl.psAppDataEntity.logicName}}',
appDeKeyFieldName: '{{#if ctrl.psAppDataEntity.keyPSAppDEField}}{{ctrl.psAppDataEntity.keyPSAppDEField.codeName}}{{/if}}',
appDeMajorFieldName: '{{#if ctrl.psAppDataEntity.majorPSAppDEField}}{{ctrl.psAppDataEntity.majorPSAppDEField.codeName}}{{/if}}',
controlService: new DataViewService<ControlVO>(ControlVO, new {{pascalCase ctrl.psAppDataEntity.codeName}}Service() ),
selections: [],
// 多数据部件分组
mdCtrlGroup: {
enableGroup: {{ctrl.enableGroup}},
groupMode: "{{ctrl.groupMode}}",
groupField: "{{lowerCase ctrl.groupPSAppDEField.codeName}}",
{{#if ctrl.groupPSCodeList}}
groupCodeList: {
codeListTag: "{{ctrl.groupPSCodeList.codeListTag}}",
codeListType: "{{ctrl.groupPSCodeList.codeListType}}",
},
{{/if}}
},
// 多数据部件排序
mdCtrlSort: {
noSort: {{ctrl.noSort}},
minorSortDir: "{{#if ctrl.minorSortDir}}{{ctrl.minorSortDir}}{{/if}}",
minorSortPSDEF: "{{ctrl.minorSortPSAppDEField.codeName}}",
},
// 多数据部件分页
mdCtrlPaging: {
enablePagingBar: true,
current: 1,
pageSize: {{#if ctrl.pagingSize}}{{ctrl.pagingSize}}{{else}}20{{/if}},
pagination: {}
},
{{#if ctrl.quickToolBarItems}}
{{#ctrl.quickToolBarItems}}
quickToolbar: [
{{#items}}
{ id:'{{id}}',name:'{{name}}',caption:'{{caption}}',groupExtractMode:'{{groupExtractMode}}',itemType:'{{itemType}}',noPrivDisplayMode:'{{noPrivDisplayMode}}',showIcon:{{showIcon}},showCaption:{{showCaption}},tooltip:'{{tooltip}}',disabled: false, visible: true, imgPath: '{{imgPath}}',iconClass: '{{iconClass}}',xDataControlName:'{{xDataControlName}}',{{#if uIAction}}uIAction:{codeName:'{{uIAction.codeName}}',fullCodeName:'{{uIAction.fullCodeName}}',uIActionMode:'{{uIAction.uIActionMode}}',actionTarget:'{{uIAction.actionTarget}}',uIActionTag:'{{uIAction.uIActionTag}}',dataAccessAction:'{{uIAction.dataAccessAction}}',uIActionType:'{{uIAction.uIActionType}}'}{{/if}} },
{{/items}}
],
{{/ctrl.quickToolBarItems}}
{{/if}}
{{#if ctrl.batchToolBarItems}}
{{#ctrl.batchToolBarItems}}
batchToolbar: [
{{#items}}
{ id:'{{id}}',name:'{{name}}',caption:'{{caption}}',groupExtractMode:'{{groupExtractMode}}',itemType:'{{itemType}}',noPrivDisplayMode:'{{noPrivDisplayMode}}',showIcon:{{showIcon}},showCaption:{{showCaption}},tooltip:'{{tooltip}}',disabled: false, visible: true, imgPath: '{{imgPath}}',iconClass: '{{iconClass}}',xDataControlName:'{{xDataControlName}}',{{#if uIAction}}uIAction:{codeName:'{{uIAction.codeName}}',fullCodeName:'{{uIAction.fullCodeName}}',uIActionMode:'{{uIAction.uIActionMode}}',actionTarget:'{{uIAction.actionTarget}}',uIActionTag:'{{uIAction.uIActionTag}}',dataAccessAction:'{{uIAction.dataAccessAction}}',uIActionType:'{{uIAction.uIActionType}}'}{{/if}} },
{{/items}}
],
{{/ctrl.batchToolBarItems}}
{{/if}}
// 界面行为
uIActions: {
{{#each ctrl.psDEDataViewItems as | dataViewItem |}}
{{#and (eq dataViewItem.itemType 'ACTIONITEM') dataViewItem.psDEUIActionGroup dataViewItem.psDEUIActionGroup.psUIActionGroupDetails}}
{{#dataViewItem}}
{{#each dataViewItem.psDEUIActionGroup.psUIActionGroupDetails as | uiActionDetail |}}
{{#if uiActionDetail.getPSUIAction}}
{{#uiActionDetail.getPSUIAction}}
{{uIActionTag}}: { caption: '{{caption}}', tooltip: '{{tooltip}}', showCaption: {{../showCaption}}, showIcon: {{../showIcon}}, uIActionTag: '{{uIActionTag}}', uIActionType: '{{uIActionType}}', dataAccessAction: '{{dataAccessAction}}', noPrivDisplayMode: '{{noPrivDisplayMode}}', uIActionMode: '{{uIActionMode}}', disabled: false, visible: true, {{#if coutnerId}}counterId: '{{counterId}}', {{/if}}{{#if psSysImage}}cssClass: '{{psSysImage.cssClass}}', {{#if psSysImage.cssClass}}{{/if}}{{#if psSysImage.imagePath}}imagePath: '{{psSysImage.imagePath}}'{{/if}}{{/if}} },
{{/uiActionDetail.getPSUIAction}}
{{/if}}
{{/each}}
{{/dataViewItem}}
{{/and}}
{{/each}}
}
}
\ No newline at end of file
<script setup lang="ts">
import { Subject } from 'rxjs';
import { ctrlState } from './{{spinalCase ctrl.codeName}}-dataview-state';
import { DataViewControl, IActionParam, IParam, IContext, ControlAction } from '@core';
{{#if ctrl.itemPSLayoutPanel}}
{{#ctrl.itemPSLayoutPanel}}
import { {{codeName}}Panel } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-panel';
{{/ctrl.itemPSLayoutPanel}}
{{/if}}
{{#if ctrl.batchToolBarItems}}
import AppToolbar from '@components/common/app-toolbar.vue';
{{/if}}
interface Props {
name:string,
parent: IParam;
context: IContext;
viewParams: IParam;
multiple?: boolean;
selectFirstDefault?: boolean;
controlAction: ControlAction;
showBusyIndicator?: boolean;
viewSubject: Subject<IActionParam>;
}
const props = withDefaults(defineProps < Props > (), {
viewSubject: () => new Subject < IActionParam > (),
showBusyIndicator: true,
multiple: false,
selectFirstDefault: false,
})
// emit声明
interface CtrlEmit {
(name: "ctrlEvent", value: IActionParam): void;
}
// emit声明
const emit = defineEmits<CtrlEmit>();
// 安装功能模块,提供状态和能力
const { name, state, onUIAction, newRow, remove, save, load, refresh, getData, xDataControl, exportExcel, onToolbarEvent, useCustom } = new DataViewControl(ctrlState, props, emit).moduleInstall();
const { onDataViewItemSelected, isSelected } = useCustom();
// 暴露内部状态及能力
defineExpose({ name, state, newRow, remove, save, load, refresh, getData, exportExcel });
</script>
<template>
<div class="app-dataview" ref="xDataControl">
<a-row
v-show="state.items && state.items.length > 0"
class="dataview-container"
:gutter="20"
justify="start">
<template v-for="(item, index) in state.items" :key="index">
<a-col {{#if ctrl.cardWidth}}:style="{ width: '{{ctrl.cardWidth}}px'{{#if ctrl.cardHeight}}, height: '{{ctrl.cardHeight}}px'{{/if}} }"{{else}}:span="6"{{#if ctrl.cardHeight}}:style="{ height: '{{ctrl.cardHeight}}px' }"{{/if}}{{/if}}>
<a-card :class="['app-dataview-item', isSelected(item) ? 'app-dataview-item__selection' : '']" @click="(event) => onDataViewItemSelected(item, event)">
<template #title>
\{{item.srfmajortext}}
</template>
<template v-if="state.uIActions && Object.keys(state.uIActions).length" #actions>
<div class="app-dataview-item__actions">
<template v-for="(action, index) of state.uIActions" :key="index">
<span
v-if="item[action.uIActionTag][0].visible"
:class="['app-dataview-item__action', item[action.uIActionTag][0].disabled ? 'disabled' : '']"
:title="action.tooltip ? action.tooltip : action.caption"
@click.stop="(event) => onUIAction(item, item[action.uIActionTag][0], event)">
<template v-if="action.showIcon">
<i v-if="action.cssClass" :class="action.cssClass" />
<img v-if="action.imagePath" :src="action.imagePath" />
</template>
<span v-if="action.showCaption" class="text">\{{action.caption}}</span>
</span>
</template>
</div>
</template>
<span class="app-dataview-item__description">\{{item.srfdescription}}</span>
</a-card>
</a-col>
</template>
</a-row>
<div v-if="!state.items || state.items.length === 0" class="not-data">
<span class="empty-text">
{{#if ctrl.emptyText}}
{{ctrl.emptyText}}
{{else}}
无数据
{{/if}}
</span>
{{#if ctrl.quickToolBarItems}}
<AppToolbar
mode="button"
class="app-dataview-quicktoolbar"
name="{{ctrl.quickToolBarName}}"
:actionModel="state.quickToolbar"
@onToolbarEvent="onToolbarEvent"
></AppToolbar>
{{/if}}
</div>
</div>
</template>
<style lang="scss">
</style>
\ No newline at end of file
...@@ -13,17 +13,16 @@ export class ControlVO extends ControlVOBase { ...@@ -13,17 +13,16 @@ export class ControlVO extends ControlVOBase {
{{/each}} {{/each}}
} }
{{#each ctrl.psDEListDataItems as | dataItem |}}
{{#each ctrl.psDEListDataItems as | dataItem |}} {{#if dataItem.psAppDEField}}
{{#if dataItem.psAppDEField}}
get {{lowerCase dataItem.name}}() { get {{lowerCase dataItem.name}}() {
return this.$DO.{{lowerCase dataItem.psAppDEField.codeName}}; return this.$DO.{{lowerCase dataItem.psAppDEField.codeName}};
} }
set {{lowerCase dataItem.name}}(value: any) { set {{lowerCase dataItem.name}}(value: any) {
this.$DO.{{lowerCase dataItem.psAppDEField.codeName}} = value; this.$DO.{{lowerCase dataItem.psAppDEField.codeName}} = value;
} }
{{/if}} {{/if}}
{{#if dataItem.customCode}} {{#if dataItem.customCode}}
get {{lowerCase dataItem.name}}() { get {{lowerCase dataItem.name}}() {
let scriptCode: any = `{{dataItem.scriptCode}}`; let scriptCode: any = `{{dataItem.scriptCode}}`;
if (scriptCode.indexOf('return') !== -1) { if (scriptCode.indexOf('return') !== -1) {
...@@ -31,9 +30,9 @@ export class ControlVO extends ControlVOBase { ...@@ -31,9 +30,9 @@ export class ControlVO extends ControlVOBase {
} }
return scriptCode; return scriptCode;
} }
{{/if}} {{/if}}
{{/each}} {{/each}}
} }
export const ctrlState = { export const ctrlState = {
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册