提交 57c405a2 编写于 作者: Mosher's avatar Mosher

add:新增首页视图菜单计数器支持

上级 13aaddb5
...@@ -17,6 +17,25 @@ ...@@ -17,6 +17,25 @@
isLoadDefault: {{#if (or page.loadDefault (eq page.loadDefault false))}}{{page.loadDefault}}{{else}}true{{/if}}, isLoadDefault: {{#if (or page.loadDefault (eq page.loadDefault false))}}{{page.loadDefault}}{{else}}true{{/if}},
appViewNavContexts:{{> @macro/front-end/common/navparam.hbs appNavParams=page.psAppViewNavContexts}}, appViewNavContexts:{{> @macro/front-end/common/navparam.hbs appNavParams=page.psAppViewNavContexts}},
appViewNavParams:{{> @macro/front-end/common/navparam.hbs appNavParams=page.psAppViewNavParams}}, appViewNavParams:{{> @macro/front-end/common/navparam.hbs appNavParams=page.psAppViewNavParams}},
{{#if page.psAppCounterRefs}}
appCounterRefs: [
{{#each page.psAppCounterRefs as | counterRef |}}
{{#if counterRef.psAppCounter}}
{{#with counterRef.psAppCounter as | counter |}}
{
id: '{{counterRef.id}}',
getAction: '{{counter.getPSAppDEAction.codeName}}',
timer: {{#if counter.timer}}{{counter.timer}}{{else}}6000{{/if}},
{{#if counter.psAppDataEntity}}
deCodeName: '{{lowerCase counter.psAppDataEntity.codeName}}'
{{/if}}
}{{#unless @last}},{{/unless}}
{{/with}}
{{/if}}
{{/each}}
],
counterServices: [],
{{/if}}
{{!-- 快速分组代码表 --}} {{!-- 快速分组代码表 --}}
enableQuickGroup: {{#if page.enableQuickGroup}}true{{else}}false{{/if}}, enableQuickGroup: {{#if page.enableQuickGroup}}true{{else}}false{{/if}},
{{#if page.quickGroupPSCodeList}} {{#if page.quickGroupPSCodeList}}
......
import { SyncSeriesHook } from "qx-util"; import { SyncSeriesHook } from "qx-util";
import router from "@/router"; import router from "@/router";
import { AppBase, IParam, ViewDetail, IApp, IOpenViewService, deepCopy, Http, IAppAuthService, IAppNotificationService, IAppFuncService, IAppActionService, IAppCodeListService } from "@core"; import { AppBase, IParam, ViewDetail, IApp, IOpenViewService, deepCopy, Http, IAppAuthService, IAppNotificationService, IAppFuncService, IAppActionService, IAppCodeListService, IAppCounterService } from "@core";
import { AppFuncConfig, AppViewConfig, AppEntityConfig } from './config'; import { AppFuncConfig, AppViewConfig, AppEntityConfig } from './config';
import { DataServiceRegister, UIServiceRegister } from "./register"; import { DataServiceRegister, UIServiceRegister } from "./register";
import { Environment } from "@/environments/environment"; import { Environment } from "@/environments/environment";
import { AppActionService, AppAuthService, AppCodeListService, AppFuncService, AppNotificationService, OpenViewService } from "@/service"; import { AppActionService, AppAuthService, AppCodeListService, AppCounterService, AppFuncService, AppNotificationService, OpenViewService } from "@/service";
export class App extends AppBase implements IApp { export class App extends AppBase implements IApp {
...@@ -130,6 +130,19 @@ export class App extends AppBase implements IApp { ...@@ -130,6 +130,19 @@ export class App extends AppBase implements IApp {
return AppCodeListService.getInstance(); return AppCodeListService.getInstance();
} }
/**
* @description 获取计数器服务
* @param {IParam} counterRef 计数器引用
* @param {IParam} [context={}] 应用上下文
* @param {IParam} [viewParams={}] 视图参数
* @return {*} {IAppCounterService}
* @memberof App
*/
public getCounterService(counterRef: IParam, context: IParam = {}, viewParams: IParam = {}): IAppCounterService {
// TODO 暂时使用构造器构造
return new AppCounterService(counterRef, context, viewParams);
}
/** /**
* 获取UI服务 * 获取UI服务
* *
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { IActionParam, IParam } from '@core'; import { IActionParam, IParam } from '@core';
interface MenuCenterProps { interface MenuCenterProps {
menus: IParam[]; menus: IParam[];
counterData?: IParam;
} }
const props = withDefaults(defineProps<MenuCenterProps>(), {}); const props = withDefaults(defineProps<MenuCenterProps>(), {});
interface menuCenterEmit { interface menuCenterEmit {
...@@ -30,6 +31,13 @@ const getLayout = (item: IParam ,name: string) => { ...@@ -30,6 +31,13 @@ const getLayout = (item: IParam ,name: string) => {
return {span: xs, offset: item.layoutPos.xsOffset}; return {span: xs, offset: item.layoutPos.xsOffset};
} }
} }
const hasCounter = (item: IParam): boolean => {
if (item.counterId && props.counterData && props.counterData.hasOwnProperty(item.counterId)) {
return true;
}
return false;
}
</script> </script>
<template> <template>
...@@ -43,10 +51,19 @@ const getLayout = (item: IParam ,name: string) => { ...@@ -43,10 +51,19 @@ const getLayout = (item: IParam ,name: string) => {
> >
<template v-if="Object.is(menu.layoutPos.layout, 'FLEX')"> <template v-if="Object.is(menu.layoutPos.layout, 'FLEX')">
<a-space :size="24" class="app-menu__center--flex"> <a-space :size="24" class="app-menu__center--flex">
<template v-for="item in menu.items"> <template v-for="(item, index) in menu.items">
<template v-if="hasCounter(item)">
<a-badge :key="index" class="app-menu__center-badge" :count="counterData[item.counterId]">
<a-button v-if="!item.hidden" size="large" :class="['app-menu-item', item.itemSysCss]" @click="onClick(item)"> <a-button v-if="!item.hidden" size="large" :class="['app-menu-item', item.itemSysCss]" @click="onClick(item)">
{{ item.caption }} {{ item.caption }}
</a-button> </a-button>
</a-badge>
</template>
<template v-else>
<a-button :key="index" v-if="!item.hidden" size="large" :class="['app-menu-item', item.itemSysCss]" @click="onClick(item)">
{{ item.caption }}
</a-button>
</template>
</template> </template>
</a-space> </a-space>
</template> </template>
...@@ -63,11 +80,27 @@ const getLayout = (item: IParam ,name: string) => { ...@@ -63,11 +80,27 @@ const getLayout = (item: IParam ,name: string) => {
:class="['app-menu-item', item.itemSysCss]" :class="['app-menu-item', item.itemSysCss]"
@click="onClick(item)" @click="onClick(item)"
> >
<template v-if="hasCounter(item)">
<a-badge class="app-menu__center-badge" :count="counterData[item.counterId]">
<a-button size="large"> <a-button size="large">
{{ item.caption }} {{ item.caption }}
</a-button> </a-button>
</a-badge>
</template>
<template v-else>
<a-button size="large">
{{ item.caption }}
</a-button>
</template>
</a-col> </a-col>
</a-row> </a-row>
</template> </template>
</a-card> </a-card>
</template> </template>
<style lang="scss">
.app-menu__center-badge {
width: 100%;
}
</style>
...@@ -3,10 +3,18 @@ import { IParam } from "@core"; ...@@ -3,10 +3,18 @@ import { IParam } from "@core";
interface Props{ interface Props{
items: IParam[], items: IParam[],
collapsed?: boolean, collapsed?: boolean,
counterData?: IParam
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
collapsed: false, collapsed: false,
}); });
const hasCounter = (item: IParam): boolean => {
if (item.counterId && props.counterData && props.counterData.hasOwnProperty(item.counterId)) {
return true;
}
return false;
}
</script> </script>
<template> <template>
...@@ -16,7 +24,16 @@ const props = withDefaults(defineProps<Props>(), { ...@@ -16,7 +24,16 @@ const props = withDefaults(defineProps<Props>(), {
<template #icon> <template #icon>
<AppIconText :iconClass="item.iconClass" :imgPath="item.imgPath" :text="collapsed ? item.caption[0] : undefined"/> <AppIconText :iconClass="item.iconClass" :imgPath="item.imgPath" :text="collapsed ? item.caption[0] : undefined"/>
</template> </template>
<a-tooltip :title="item.tooltip">{{item.caption}}</a-tooltip> <a-tooltip :title="item.tooltip">
<template v-if="hasCounter(item)">
<a-badge :count="counterData[item.counterId]">
{{item.caption}}
</a-badge>
</template>
<template v-else>
{{item.caption}}
</template>
</a-tooltip>
</a-menu-item> </a-menu-item>
</template> </template>
<template v-else> <template v-else>
...@@ -24,8 +41,19 @@ const props = withDefaults(defineProps<Props>(), { ...@@ -24,8 +41,19 @@ const props = withDefaults(defineProps<Props>(), {
<template #icon> <template #icon>
<AppIconText :iconClass="item.iconClass" :imgPath="item.imgPath" :text="collapsed ? item.caption[0] : undefined"/> <AppIconText :iconClass="item.iconClass" :imgPath="item.imgPath" :text="collapsed ? item.caption[0] : undefined"/>
</template> </template>
<template #title><a-tooltip :title="item.tooltip">{{item.caption}}</a-tooltip></template> <template #title>
<AppMenuItem :items="item.items" /> <a-tooltip :title="item.tooltip">
<template v-if="hasCounter(item)">
<a-badge :count="counterData[item.counterId]">
{{item.caption}}
</a-badge>
</template>
<template v-else>
{{item.caption}}
</template>
</a-tooltip>
</template>
<AppMenuItem :items="item.items" :counterData="counterData" />
</a-sub-menu> </a-sub-menu>
</template> </template>
</template> </template>
......
import { IParam } from "..";
export interface IAppCounterService {
/**
* 执行计数器
*
* @param {IParam} [context]
* @param {IParam} [viewParams]
* @return {*} {Promise<void>}
* @memberof IAppCounterService
*/
execute(context?: IParam, viewParams?: IParam): Promise<void>;
}
\ No newline at end of file
...@@ -6,3 +6,4 @@ export * from './i-data-service-register'; ...@@ -6,3 +6,4 @@ export * from './i-data-service-register';
export * from './i-ui-service-register'; export * from './i-ui-service-register';
export * from './i-app-auth-service'; export * from './i-app-auth-service';
export * from './i-app-code-list-service'; export * from './i-app-code-list-service';
export * from './i-app-counter-service';
\ No newline at end of file
import { IApp, IAppFuncService, IOpenViewService, ViewDetail } from "@core"; import { IApp, IAppFuncService, IOpenViewService, ViewDetail } from "@core";
import { IAppActionService, IAppAuthService, IAppCodeListService, IAppNotificationService, IParam } from "@core/interface"; import { IAppActionService, IAppAuthService, IAppCodeListService, IAppCounterService, IAppNotificationService, IParam } from "@core/interface";
/** /**
* 应用基类 * 应用基类
...@@ -99,6 +99,18 @@ export abstract class AppBase implements IApp { ...@@ -99,6 +99,18 @@ export abstract class AppBase implements IApp {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
/**
* @description 获取计数器服务
* @param {IParam} counterRef 计数器引用
* @param {IParam} [context={}] 应用上下文
* @param {IParam} [viewParams={}] 视图参数
* @return {*} {IAppCounterService}
* @memberof AppBase
*/
public getCounterService(counterRef: IParam, context: IParam = {}, viewParams: IParam = {}): IAppCounterService {
throw new Error("Method not implemented.");
}
/** /**
* 获取UI服务 * 获取UI服务
* *
......
...@@ -200,7 +200,18 @@ export class ViewBase { ...@@ -200,7 +200,18 @@ export class ViewBase {
* *
* @memberof ViewBase * @memberof ViewBase
*/ */
public useCounterService() { } public useCounterService() {
const { appCounterRefs, context, viewParams } = this.state;
const { counterServices } = toRefs(this.state);
if (appCounterRefs && appCounterRefs.length) {
appCounterRefs.forEach((counterRef: IParam) => {
const counterSerivce = App.getCounterService(counterRef, context, viewParams);
counterSerivce.execute().then(() => {
counterServices.value.push(counterSerivce);
});
});
}
}
/** /**
* 处理部件事件 * 处理部件事件
......
import { hasFunction, IParam } from "@core";
export class AppCounterService {
/**
* @description 定时器延时时间
* @protected
* @type {number}
* @memberof AppCounterService
*/
protected delayTime: number = 6000;
/**
* @description 计数器对象
* @protected
* @type {*}
* @memberof AppCounterService
*/
protected timer: any = null;
/**
* @description 获取数据行为
* @protected
* @type {string}
* @memberof AppCounterService
*/
protected getAction: string = '';
/**
* @description 计数器唯一标识
* @private
* @type {string}
* @memberof AppCounterService
*/
private counterId: string = '';
/**
* @description 计数器实体标识
* @private
* @type {string}
* @memberof AppCounterService
*/
private deCodeName: string = '';
/**
* @description 实体服务
* @private
* @type {*}
* @memberof AppCounterService
*/
private entityService: any;
/**
* @description 计数器数据
* @private
* @type {*}
* @memberof AppCounterService
*/
private counterData: any;
/**
* @description 获取计数器数据
* @readonly
* @memberof AppCounterService
*/
get data(): any {
return this.counterData;
}
/**
* Creates an instance of AppCounterService.
* @param {IParam} counterRef 计数器
* @param {IParam} [context={}] 应用上下文
* @param {IParam} [viewParams={}] 视图参数
* @memberof AppCounterService
*/
constructor (counterRef: IParam, private context: IParam = {}, private viewParams: IParam = {}) {
this.delayTime = counterRef.timer;
this.getAction = counterRef.getAction;
this.deCodeName = counterRef.deCodeName;
this.counterId = counterRef.id;
}
/**
* @description 执行计数器
* @memberof AppCounterService
*/
async execute() {
await this.getCounterData();
this.destory();
this.timer = setInterval(() => {
this.getCounterData();
}, this.delayTime);
}
/**
* @description 获取计数器数据
* @private
* @return {*}
* @memberof AppCounterService
*/
private async getCounterData() {
// 获取实体服务
if (this.deCodeName && !this.entityService) {
this.entityService = await App.getDataService(this.deCodeName, this.context);
}
if (!this.entityService || !this.getAction) {
return;
}
if (hasFunction(this.entityService, this.getAction)) {
try {
const response: any = await this.entityService[this.getAction](this.context, this.viewParams);
if (response && response.status === 200 && response.data) {
this.counterData = response.data;
}
} catch (error: any) {
console.error(error);
}
}
}
/**
* @description 销毁定时器
* @memberof AppCounterService
*/
destory() {
if (this.timer) {
clearInterval(this.timer);
}
}
}
\ No newline at end of file
...@@ -4,3 +4,4 @@ export { AppActionService } from './app-action-service'; ...@@ -4,3 +4,4 @@ export { AppActionService } from './app-action-service';
export { AppAuthService } from './app-auth-service'; export { AppAuthService } from './app-auth-service';
export { AppFuncService } from './app-func-service'; export { AppFuncService } from './app-func-service';
export { AppNotificationService } from './app-notification-service'; export { AppNotificationService } from './app-notification-service';
export { AppCounterService } from './app-counter-service';
\ No newline at end of file
...@@ -37,6 +37,15 @@ const collapsed: Ref<boolean> = ref(false); ...@@ -37,6 +37,15 @@ const collapsed: Ref<boolean> = ref(false);
const collapsedChange = () => { const collapsedChange = () => {
collapsed.value = !collapsed.value; collapsed.value = !collapsed.value;
} }
{{#if page.psAppCounterRefs}}
const counterData = computed(() => {
const { counterServices } = toRefs(state);
if (counterServices.value && counterServices.value.length) {
return counterServices.value[0].data;
}
return {};
})
{{/if}}
</script> </script>
<template> <template>
...@@ -62,6 +71,9 @@ const collapsedChange = () => { ...@@ -62,6 +71,9 @@ const collapsedChange = () => {
ref="menu" ref="menu"
name="{{name}}" name="{{name}}"
:context="state.context" :context="state.context"
{{#if page.psAppCounterRefs}}
:counterData="counterData"
{{/if}}
:collapsed="collapsed" :collapsed="collapsed"
:menuAlign="state.menuAlign" :menuAlign="state.menuAlign"
:defaultView="state.defaultView" :defaultView="state.defaultView"
......
...@@ -11,6 +11,7 @@ interface Props { ...@@ -11,6 +11,7 @@ interface Props {
viewParams?: IParam; viewParams?: IParam;
parent: IParam; parent: IParam;
viewSubject: Subject<IActionParam>; viewSubject: Subject<IActionParam>;
counterData?: IParam;
} }
const props = withDefaults(defineProps < Props > (), { const props = withDefaults(defineProps < Props > (), {
menuAlign: "LEFT", menuAlign: "LEFT",
...@@ -30,11 +31,11 @@ defineExpose({ name, state, load }); ...@@ -30,11 +31,11 @@ defineExpose({ name, state, load });
<template> <template>
<div v-if="Object.is('CENTER', state.menuAlign)" class="app-menu app-menu--center{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}"> <div v-if="Object.is('CENTER', state.menuAlign)" class="app-menu app-menu--center{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<AppMenuCenter :menus="state.menus" @onMenuSelect="onMenuSelect" /> <AppMenuCenter :menus="state.menus" @onMenuSelect="onMenuSelect" :counterData="counterData" />
</div> </div>
<a-menu v-else class="app-menu{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}" v-model:openKeys="state.defaultOpens" v-model:selectedKeys="state.defaultSelect" <a-menu v-else class="app-menu{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}" v-model:openKeys="state.defaultOpens" v-model:selectedKeys="state.defaultSelect"
:mode="Object.is('LEFT', state.menuAlign) ? 'inline' : 'horizontal'" @select="onMenuSelect"> :mode="Object.is('LEFT', state.menuAlign) ? 'inline' : 'horizontal'" @select="onMenuSelect">
<AppMenuItem :items="state.menus" :collapsed="collapsed" /> <AppMenuItem :items="state.menus" :collapsed="collapsed" :counterData="counterData" />
</a-menu> </a-menu>
</template> </template>
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册