提交 822cb13e 编写于 作者: Mosher's avatar Mosher

add:新增列表导航视图、列表导航部件支持

上级 2ae146f9
<script setup lang="ts">
</script>
<template>
<AppViewBaseLayout>
<template v-slot:header-left>
<slot name="caption" />
</template>
<template v-slot:header-right>
<slot name="toolbar" />
</template>
<template v-slot:header-bottom>
<slot name="topMessage" />
<slot name="searchForm" />
</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">
</style>
\ No newline at end of file
......@@ -14,4 +14,5 @@ export * from './portal-view'
export * from './tab-exp-view'
export * from './list-view'
export * from './panel-view'
export * from './pickup-tree-view'
\ No newline at end of file
export * from './pickup-tree-view'
export * from './list-exp-view'
\ No newline at end of file
export * from './list-exp-view-prop';
export * from './list-exp-view-state';
export { ListExpView } from './list-exp-view';
\ No newline at end of file
import { ExpViewProps } from "../exp-view";
/**
* 列表导航视图输入参数
*
* @export
* @interface ListExpViewProps
* @extends {ExpViewProps}
*/
export interface ListExpViewProps extends ExpViewProps {
}
\ No newline at end of file
import { ExpViewState } from "../exp-view";
/**
* 列表导航视图状态
*
* @export
* @interface ListExpViewState
* @extends {ExpViewState}
*/
export interface ListExpViewState extends ExpViewState {
}
\ No newline at end of file
import { ExpView } from "../exp-view";
import { ListExpViewProps } from "./list-exp-view-prop";
import { ListExpViewState } from "./list-exp-view-state";
/**
* 列表导航视图
*
* @export
* @class ListExpView
* @extends {ExpView}
*/
export class ListExpView extends ExpView {
/**
* 列表导航视图状态
*
* @type {ListExpViewState}
* @memberof ListExpView
*/
public declare state: ListExpViewState;
/**
* 列表导航视图输入参数
*
* @type {ListExpViewProps}
* @memberof ListExpView
*/
public declare props: ListExpViewProps;
}
\ No newline at end of file
......@@ -21,4 +21,44 @@ export interface ExpBarControlState extends MainControlState {
* @memberof ExpBarControlState
*/
split: number;
/**
* 导航视图标识
*
* @type {string}
* @memberof ExpBarControlState
*/
navViewName: string;
/**
* 导航过滤参数
*
* @type {string}
* @memberof ExpBarControlState
*/
navFilter?: string;
/**
* 导航关系
*
* @type {string}
* @memberof ExpBarControlState
*/
navPSDer?: string;
/**
* 导航上下文
*
* @type {IParam}
* @memberof ExpBarControlState
*/
navigateContext?: IParam;
/**
* 导航视图参数
*
* @type {IParam}
* @memberof ExpBarControlState
*/
navigateParams?: IParam;
}
\ No newline at end of file
import { IActionParam, IParam, MainControl } from "@core";
import { IActionParam, IParam, MainControl, deepCopy, isExistAndNotEmpty, UIUtil } from "@core";
import { ExpBarControlProps } from "./exp-bar-control-prop";
import { ExpBarControlState } from "./exp-bar-control-state";
......@@ -16,6 +16,14 @@ export class ExpBarControl extends MainControl {
*/
public declare state: ExpBarControlState;
/**
* 部件输入参数
*
* @type {ExpBarControlProps}
* @memberof ExpBarControl
*/
public declare props: ExpBarControlProps;
/**
* 通知状态
*
......@@ -23,10 +31,36 @@ export class ExpBarControl extends MainControl {
* @memberof ViewEngine
*/
public next({ tag, action, data }: { tag: string, action: string, data: any }): void {
const { viewSubject } = this.props;
const { viewSubject } = this.state;
viewSubject.next({ tag: tag, action: action, data: data })
}
/**
* 搜索
*
* @param {*} value
* @param {MouseEvent} event
* @memberof TreeExpBarControl
*/
public search(value: any, event: MouseEvent) { }
/**
* 使用搜索模块
*
* @protected
* @return {*}
* @memberof TreeExpBarControl
*/
protected useSearch() {
const search = (value: any, event: MouseEvent) => {
if (this.xDataControl) {
this.next({ tag: this.xDataControl.name, action: 'load', data: { query: value } });
}
}
this.search = search;
return search;
}
/**
* @description 导航部件初始化
* @protected
......@@ -88,7 +122,59 @@ export class ExpBarControl extends MainControl {
* @param {any[]} data
* @memberof ExpBarControl
*/
protected onSelectionChange(data: any[]) { }
protected onSelectionChange(data: any[]) {
// 无选中数据不处理
if (data.length === 0) {
this.calcToolbarItemState(true);
return;
}
// 选中数据
const arg: IParam = data[0];
if (!this.xDataControl || !this.xDataControl.state) {
console.warn('未获取到数据部件');
return;
}
// 上下文和视图参数
const tempContext = deepCopy(this.state.context);
const tempViewParams = deepCopy(this.state.viewParams);
const appDeCodeName: string = this.xDataControl.state.appDeCodeName?.toLowerCase() || '';
// 数据主键
const parentKey: any = appDeCodeName && isExistAndNotEmpty(arg[appDeCodeName]) ? arg[appDeCodeName] : arg.srfkey;
if (isExistAndNotEmpty(appDeCodeName)) {
Object.assign(tempContext, {
[appDeCodeName]: parentKey,
srfparentdename: appDeCodeName,
srfparentdemapname: this.xDataControl.state.deName,
srfparentkey: parentKey
});
const { navFilter, navPSDer, navigateContext, navigateParams } = this.state;
// 导航过滤项
if (navFilter) {
Object.assign(tempViewParams, { [navFilter]: parentKey });
}
// 导航关系
if (navPSDer) {
Object.assign(tempViewParams, { [navPSDer]: parentKey });
}
// 导航上下文
if (navigateContext) {
const _context = UIUtil.computedNavData(arg, this.state.context, this.state.viewParams, navigateContext);
Object.assign(tempContext, _context);
}
// 导航视图参数
if (navigateParams) {
const _params = UIUtil.computedNavData(arg, this.state.context, this.state.viewParams, navigateParams);
Object.assign(tempViewParams, _params);
}
}
const { selection } = toRefs(this.state);
Object.assign(selection.value, {
context: tempContext,
key: parentKey ? parentKey : this.state.navViewName,
viewParams: tempViewParams
});
this.calcToolbarItemState(false);
}
/**
* @description 计算工具栏权限
......@@ -138,6 +224,7 @@ export class ExpBarControl extends MainControl {
this.useExpCtrlInit();
return {
...superParams,
search: this.search.bind(this),
onToolbarEvent: this.onToolbarEvent.bind(this)
};
}
......
......@@ -17,3 +17,4 @@ export * from './tab-exp-panel-control'
export * from './tab-view-panel-control'
export * from './list-control'
export * from './panel-control'
export * from './list-exp-bar-control'
\ No newline at end of file
......@@ -151,7 +151,7 @@ export class ListControl extends MDControl {
this.emit("ctrlEvent", {
tag: this.props.name,
action: "selectionChange",
data: [deepCopy(items.value[0])],
data: [items.value[0]],
});
}
}
......
export * from './list-exp-bar-control-prop';
export * from './list-exp-bar-control-state';
export { ListExpBarControl } from './list-exp-bar-control';
\ No newline at end of file
import { ExpBarControlProps } from "../exp-bar-control";
/**
* 列表导航栏部件输入参数
*
* @export
* @interface ListExpBarControlProps
* @extends {ExpBarControlProps}
*/
export interface ListExpBarControlProps extends ExpBarControlProps {
}
\ No newline at end of file
import { ExpBarControlState } from "../exp-bar-control";
/**
* 列表导航栏部件状态
*
* @export
* @interface ListExpBarControlState
* @extends {ExpBarControlState}
*/
export interface ListExpBarControlState extends ExpBarControlState {
}
\ No newline at end of file
import { ExpBarControl } from "../exp-bar-control";
import { ListExpBarControlProps } from "./list-exp-bar-control-prop";
import { ListExpBarControlState } from "./list-exp-bar-control-state";
/**
* 列表导航栏部件
*
* @export
* @class ListExpBarControl
* @extends {ExpBarControl}
*/
export class ListExpBarControl extends ExpBarControl {
/**
* 列表导航栏部件状态
*
* @type {ListExpBarControlState}
* @memberof ListExpBarControl
*/
public declare state: ListExpBarControlState;
/**
* 列表导航栏部件输入参数
*
* @type {ListExpBarControlProps}
* @memberof ListExpBarControl
*/
public declare props: ListExpBarControlProps;
}
\ No newline at end of file
import { deepCopy, ExpBarControl, IActionParam, IParam, UIUtil } from "@core";
import { deepCopy, ExpBarControl, IParam, UIUtil } from "@core";
import { TreeExpBarControlProps } from "./tree-exp-bar-control-prop";
import { TreeExpBarControlState } from "./tree-exp-bar-control-state";
......@@ -23,15 +23,6 @@ export class TreeExpBarControl extends ExpBarControl {
* @memberof TreeExpBarControl
*/
public declare props: TreeExpBarControlProps;
/**
* 搜索
*
* @param {*} value
* @param {MouseEvent} event
* @memberof TreeExpBarControl
*/
public search(value: any, event: MouseEvent) { }
/**
* 使用搜索模块
......@@ -42,10 +33,8 @@ export class TreeExpBarControl extends ExpBarControl {
*/
protected useSearch() {
const search = (value: any, event: MouseEvent) => {
const { viewSubject } = this.state;
const xData = this.xDataControl;
if (xData) {
viewSubject.next({ tag: xData.name, action: 'search', data: { query: value } });
if (this.xDataControl) {
this.next({ tag: this.xDataControl.name, action: 'search', data: { query: value } });
}
}
this.search = search;
......@@ -158,37 +147,4 @@ export class TreeExpBarControl extends ExpBarControl {
}
return { tempContext, tempViewParams };
}
/**
* @description 使用加载功能模块
* @memberof TreeExpBarControl
*/
public useLoad() {
const { viewSubject, controlName, xDataControlName } = this.state;
if (viewSubject) {
let subscription = viewSubject.subscribe(({ tag, action, data }: IActionParam) => {
if (Object.is(controlName, tag)) {
viewSubject.next({ tag: xDataControlName, action: action, data: data })
}
})
// 部件卸载时退订viewSubject
onUnmounted(() => {
subscription.unsubscribe();
})
}
}
/**
* @description 安装部件所有功能模块的方法
* @return {*}
* @memberof TreeExpBarControl
*/
public moduleInstall() {
const superParams = super.moduleInstall();
this.useLoad();
return {
...superParams,
search: this.useSearch()
};
}
}
\ No newline at end of file
......@@ -19,10 +19,13 @@ const routes = [
{{#each appEntityResource.appDataEntity.allPSAppViews as |appView|}}
{{#if (eq appView.refFlag true)}}
{{#if (or
(eq appView.viewType 'DEEDITVIEW')
(eq appView.viewType 'DEGRIDVIEW')
(eq appView.viewType 'DETREEEXPVIEW')
(eq appView.viewType 'DETREEVIEW')
(eq appView.viewType 'DEEDITVIEW')
(eq appView.viewType 'DEGRIDVIEW')
(eq appView.viewType 'DELISTVIEW')
(eq appView.viewType 'DELISTEXPVIEW')
(eq appView.viewType 'DEPANELVIEW')
(eq appView.viewType 'DETREEEXPVIEW')
(eq appView.viewType 'DETREEVIEW')
(eq appView.viewType 'DETABEXPVIEW')
) }}
{
......
.app-tree-exp-bar {
// 导航栏部件样式(导航部件基类)
.app-exp-bar {
width: 100%;
height: 100%;
// 导航右侧视图去掉padding
.tree-exp__nav-view {
// 导航右侧视图去掉padding
.exp-bar-navview {
--app-view-layout-padding: 0px;
}
.app-split {
background: #fff;
}
// 搜索栏头部
.tree-exp-bar-header {
.exp-bar-header {
display: flex;
flex-direction: column;
padding-left: 12px;
......@@ -29,7 +30,7 @@
padding: 6px 0;
}
}
.tree-exp-bar-body {
.exp-bar-body {
padding: 6px 6px 6px 0;
}
}
\ No newline at end of file
......@@ -2,7 +2,6 @@
@use './edit-form.scss';
@use './search-form.scss';
@use './app-menu.scss';
@use './app-tree-exp-bar.scss';
@use './app-tab-exp-panel.scss';
@use './app-tab-view-panel.scss';
@use './app-portlet.scss';
......@@ -10,4 +9,5 @@
@use './app-tree.scss';
@use './app-list.scss';
@use './app-panel.scss';
@use './app-pickup-view-panel.scss';
\ No newline at end of file
@use './app-pickup-view-panel.scss';
@use './app-exp-bar.scss';
\ No newline at end of file
import {{page.codeName}} from "./{{spinalCase page.codeName}}.vue";
export default {{page.codeName}};
export const viewState = {
{{#each page.ctrls as | ctrl |}}
{{#if (eq ctrl.controlType "LISTEXPBAR")}}
listExpBarName: '{{ctrl.name}}',
{{/if}}
{{/each}}
{{> @macro/front-end/views/view-base-config.hbs}}
};
\ No newline at end of file
<script setup lang="ts">
import { Subject } from 'rxjs';
import { ListExpView, IActionParam, IParam, IContext } from '@core';
import { viewState } from './{{spinalCase page.codeName}}-state';
{{#page.ctrls}}
{{#eq controlType "LISTEXPBAR"}}
import { {{codeName}}ListExpBar } from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-list-exp-bar';
{{/eq}}
{{/page.ctrls}}
interface Props {
context?: IContext;
viewParams?: IParam;
openType?: "ROUTE" | "MODAL" | "EMBED";
viewSubject?: Subject<IActionParam>;
}
const props = withDefaults(defineProps<Props>(), {
openType:'ROUTE',
viewSubject: () => new Subject<IActionParam>()
})
interface ViewEmit {
(name: "viewEvent", value: IActionParam): void;
}
const emit = defineEmits<ViewEmit>();
// 安装功能模块
const listExpView = new ListExpView(viewState, props, emit).moduleInstall()
const { state, onCtrlEvent, onToolbarEvent } = listExpView;
</script>
<template>
<AppListExpViewLayout :class="['app-list-exp-view', state.viewSysCss]">
<template v-slot:caption>
<AppIconText class="app-view__caption" size="large" :text="state.viewCaption" />
</template>
{{#page.ctrls}}
{{#eq controlType "TOOLBAR"}}
<template v-slot:toolbar>
<AppToolbar
mode="button"
name="{{lowerCase codeName}}"
:actionModel="state.toolbar"
@onToolbarEvent="onToolbarEvent"/>
</template>
{{/eq}}
{{#eq controlType "LISTEXPBAR"}}
<{{codeName}}ListExpBar
name="{{name}}"
:context="state.context"
:showBusyIndicator="true"
:viewParams="state.viewParams"
:viewSubject="state.viewSubject"
:parent="listExpView"
@ctrlEvent="onCtrlEvent"
></{{codeName}}ListExpBar>
{{/eq}}
{{/page.ctrls}}
</AppListExpViewLayout>
</template>
\ No newline at end of file
import {{ctrl.codeName}}ListExpBar from "./{{spinalCase ctrl.codeName}}-list-exp-bar.vue";
export { {{ctrl.codeName}}ListExpBar };
export const ctrlState = {
controlCodeName: '{{ctrl.codeName}}',
controlName: '{{ctrl.name}}',
counter: 0,
enableSearch: {{ctrl.enableSearch}},
xDataControlName: '{{ctrl.xDataControlName}}',
selection: {},
searchValue: '',
showTitleBar: {{#if ctrl.showTitleBar}}true{{else}}false{{/if}},
split: 0.2,
title: '{{ctrl.title}}',
titleRes: '{{#if ctrl.titlePSLanguageRes}}{{ctrl.titlePSLanguageRes.lanResTag}}{{/if}}',
{{#each ctrl.psControls as | childCtrl |}}
{{#eq childCtrl.controlType 'TOOLBAR'}}
toolbar: [
{{#childCtrl.psDEToolbarItems}}
{ 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 psUIAction}}uIAction:{codeName:'{{psUIAction.codeName}}',fullCodeName:'{{psUIAction.fullCodeName}}',uIActionMode:'{{psUIAction.uIActionMode}}',actionTarget:'{{psUIAction.actionTarget}}',uIActionTag:'{{psUIAction.uIActionTag}}',dataAccessAction:'{{psUIAction.dataAccessAction}}',uIActionType:'{{psUIAction.uIActionType}}'}{{#if psUIAction.counterId}},counterId: '{{psUIAction.counterId}}'{{/if}}{{/if}} },
{{/childCtrl.psDEToolbarItems}}
],
{{/eq}}
{{#eq childCtrl.controlType 'LIST'}}
{{childCtrl.name}}: {
action:{
loadAction: '{{childCtrl.getPSControlAction.psAppDEMethod.codeName}}',
removeAction: '{{childCtrl.removePSControlAction.psAppDEMethod.codeName}}',
updateAction: '{{childCtrl.updatePSControlAction.psAppDEMethod.codeName}}',
loadDraftAction: '{{childCtrl.getDraftPSControlAction.psAppDEMethod.codeName}}',
createAction: '{{childCtrl.createPSControlAction.psAppDEMethod.codeName}}',
fetchAction:'{{childCtrl.fetchPSControlAction.psAppDEMethod.codeName}}'
}
},
{{#and childCtrl.navPSDER childCtrl.navPSDER.minorCodeName}}
navPSDer: 'n_{{lowerCase childCtrl.navPSDER.minorCodeName}}_eq',
{{/and}}
{{#if childCtrl.navFilter}}
navFilter: '{{lowerCase childCtrl.navFilter}}',
{{/if}}
{{#if childCtrl.navPSAppView}}
navViewName: '{{childCtrl.navPSAppView.name}}',
{{/if}}
{{#if childCtrl.psNavigateContexts}}
navigateContexts: {{> @macro/front-end/common/navparam.hbs appNavParams=childCtrl.psNavigateContexts}},
{{/if}}
{{#if childCtrl.psNavigateParams}}
navigateParams: {{> @macro/front-end/common/navparam.hbs appNavParams=childCtrl.psNavigateParams}},
{{/if}}
{{/eq}}
{{/each}}
};
<script setup lang="ts">
import { Subject } from 'rxjs';
import { IActionParam, IParam, ControlAction, ListExpBarControl, IContext } from '@core';
import { ctrlState } from './{{spinalCase ctrl.codeName}}-list-exp-bar-state';
{{#ctrl.psControls}}
{{#eq controlType "LIST"}}
import { {{codeName}}List} from '@widgets/{{spinalCase psAppDataEntity.codeName}}/{{spinalCase codeName}}-list';
{{#if navPSAppView}}
import {{navPSAppView.name}} from '@views/{{spinalCase navPSAppView.psAppModule.codeName}}/{{spinalCase navPSAppView.codeName}}';
{{/if}}
{{/eq}}
{{/ctrl.psControls}}
interface Props {
name:string,
parent: IParam;
context: IContext;
viewParams?: IParam;
showBusyIndicator?: boolean;
viewMode?: number;
viewSubject: Subject<IActionParam>;
}
const props = withDefaults(defineProps < Props > (), {
viewSubject: () => new Subject < IActionParam > (),
viewMode: 0,
showBusyIndicator: true,
});
interface CtrlEmit {
(name: "ctrlEvent", value: IActionParam): void;
}
const emit = defineEmits <CtrlEmit> ();
// 安装功能模块,提供状态和能力方法
const { name, state, onCtrlEvent, xDataControl, search, onToolbarEvent } = new ListExpBarControl(ctrlState, props, emit).moduleInstall();
// 暴露内部状态及能力
defineExpose({ name, state });
</script>
<template>
<div class="app-list-exp-bar app-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<AppSplit v-model="state.split">
<template #left>
<div v-if="state.enableSearch || state.toolbar || state.showTitleBar" class="exp-bar-header">
<div class="title" v-if="state.showTitleBar"><span>\{{ state.title }}</span></div>
{{~#if ctrl.enableSearch}}
<a-input-search class="search" v-model:value="state.searchValue" @search="search" />
{{/if}}
{{#each ctrl.psControls as | childCtrl |}}
{{#eq childCtrl.controlType 'TOOLBAR'}}
{{!-- TODO 计数器 --}}
<AppToolbar
mode="button"
name="{{lowerCase childCtrl.codeName}}"
:actionModel="state.toolbar"
@onToolbarEvent="onToolbarEvent">
</AppToolbar>
{{/eq}}
{{/each}}
</div>
<div class="exp-bar-body">
{{#ctrl.psControls}}
{{#eq controlType "LIST"}}
<{{codeName}}List
ref="xDataControl"
name="{{name}}"
:controlAction="state.{{name}}.action"
:context="state.context"
:viewParams="state.viewParams"
:viewSubject="state.viewSubject"
:selectFirstDefault="true"
:isBranchAvailable="true"
:parent="parent"
@ctrlEvent="onCtrlEvent"
></{{codeName}}List>
{{/eq}}
{{/ctrl.psControls}}
</div>
</template>
<template #right>
{{#ctrl.psControls}}
{{#eq controlType "LIST"}}
{{#if navPSAppView}}
<{{navPSAppView.name}}
v-if="state.selection && state.selection.key"
class="list-exp-bar-navview exp-bar-navview"
:context="state.selection.context"
:key="state.selection.key"
:viewDefaultUsage="false"
:viewParams="state.selection.viewParams"
></{{navPSAppView.name}}>
{{/if}}
{{/eq}}
{{/ctrl.psControls}}
</template>
</AppSplit>
</div>
</template>
<style lang="scss" scoped>
.app-list-exp-bar {
width: 100%;
height: 100%;
}
</style>
\ No newline at end of file
......@@ -62,10 +62,10 @@ defineExpose({ name, state });
</script>
<template>
<div class="app-tree-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<div class="app-tree-exp-bar app-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<AppSplit v-model="state.split">
<template #left>
<div v-if="state.enableSearch || state.toolbar || state.showTitleBar" class="tree-exp-bar-header">
<div v-if="state.enableSearch || state.toolbar || state.showTitleBar" class="exp-bar-header">
<div class="title" v-if="state.showTitleBar"><span>\{{ state.title }}</span></div>
{{~#if ctrl.enableSearch}}
<a-input-search class="search" v-model:value="state.searchValue" @search="search" />
......@@ -82,7 +82,7 @@ defineExpose({ name, state });
{{/eq}}
{{/each}}
</div>
<div class="tree-exp-bar-body">
<div class="exp-bar-body">
{{#ctrl.psControls}}
{{#eq controlType "TREEVIEW"}}
<{{codeName}}Tree
......@@ -121,7 +121,7 @@ defineExpose({ name, state });
}}
<{{viewRef.refPSAppView.name}}
v-if="state.selection.viewName && state.selection.viewName === '{{viewRef.refPSAppView.name}}'"
class="tree-exp__nav-view"
class="tree-exp-bar-navview exp-bar-navview"
:viewDefaultUsage="false"
:context="state.selection.context"
:viewParams="state.selection.viewParams">
......@@ -129,7 +129,7 @@ defineExpose({ name, state });
{{else}}
<!-- <{{viewRef.refPSAppView.name}}
v-if="state.selection.viewName && state.selection.viewName === '{{viewRef.refPSAppView.name}}'"
class="tree-exp__nav-view"
class="tree-exp-bar-navview exp-bar-navview"
:viewDefaultUsage="false"
:context="state.selection.context"
:viewParams="state.selection.viewParams">
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册