提交 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
......@@ -15,3 +15,4 @@ export * from './tab-exp-view'
export * from './list-view'
export * from './panel-view'
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";
......@@ -24,15 +24,6 @@ export class TreeExpBarControl extends ExpBarControl {
*/
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
......@@ -21,6 +21,9 @@ const routes = [
{{#if (or
(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';
......@@ -11,3 +10,4 @@
@use './app-list.scss';
@use './app-panel.scss';
@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 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册