Browse Source

Changing the workbench layout

* 变更工作台组件.
* 工作台加入天气, 需要适配天气api接口.
* 将 withAcceptLanguage 加入axios中, 用户自行决定是否加入头.
* Platform模块新增用户收藏菜单接口,使用前需要切换目录
  PlatformManagement.HttpApi.Host,运行命令行 **dotnet ef database
  update** 或 **dotnet ef migrations script**执行迁移
pull/700/head
cKey 3 years ago
parent
commit
b5b9e66ba1
  1. 75
      apps/vue/src/api/weather/index.ts
  2. 101
      apps/vue/src/api/weather/model/index.ts
  3. 6
      apps/vue/src/components/Form/src/components/ApiTree.vue
  4. 86
      apps/vue/src/layouts/default/header/components/lock/LockModal.vue
  5. 21
      apps/vue/src/locales/lang/en/routes/dashboard.ts
  6. 21
      apps/vue/src/locales/lang/zh-CN/routes/dashboard.ts
  7. 2
      apps/vue/src/router/routes/modules/dashboard.ts
  8. 36
      apps/vue/src/store/modules/weather.ts
  9. 11
      apps/vue/src/utils/http/axios/index.ts
  10. 76
      apps/vue/src/views/dashboard/workbench/components/MenuCard.vue
  11. 86
      apps/vue/src/views/dashboard/workbench/components/MenuReference.vue
  12. 2
      apps/vue/src/views/dashboard/workbench/components/ProjectCard.vue
  13. 84
      apps/vue/src/views/dashboard/workbench/components/WorkbenchHeader.vue
  14. 47
      apps/vue/src/views/dashboard/workbench/index.vue
  15. 2
      apps/vue/types/axios.d.ts
  16. 22
      apps/vue/vite.config.ts
  17. 28
      aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin/LINGYUN/Abp/UI/Navigation/VueVbenAdmin/VueVbenAdminStandardMenuConverter.cs
  18. 3
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/en.json
  19. 3
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/zh-Hans.json
  20. 21
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuDto.cs
  21. 9
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuGetListInput.cs
  22. 11
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuSetInput.cs
  23. 8
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/IMenuAppService.cs
  24. 3
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissionDefinitionProvider.cs
  25. 4
      aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissions.cs
  26. 73
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Menus/MenuAppService.cs
  27. 1
      aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationMappingProfile.cs
  28. 4
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN.Platform.Domain.Shared.csproj
  29. 10
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Layouts/LayoutEto.cs
  30. 11
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/MenuEto.cs
  31. 14
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/RoleMenuEto.cs
  32. 14
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/UserMenuEto.cs
  33. 12
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RoleRouteEto.cs
  34. 2
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteEto.cs
  35. 5
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/UserFavoriteMenuConsts.cs
  36. 12
      aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/UserRouteEto.cs
  37. 27
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItemMappingOptions.cs
  38. 20
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/DefaultStandardMenuConverter.cs
  39. 3
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IMenuRepository.cs
  40. 6
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IStandardMenuConverter.cs
  41. 20
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IUserFavoriteMenuRepository.cs
  42. 71
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/MenuChangeHandler.cs
  43. 10
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/StandardMenu.cs
  44. 47
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/UserFavoriteMenu.cs
  45. 8
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainMappingProfile.cs
  46. 10
      aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainModule.cs
  47. 28
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContextModelBuilderExtensions.cs
  48. 9
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs
  49. 44
      aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs
  50. 28
      aspnet-core/modules/platform/LINGYUN.Platform.HttpApi/LINGYUN/Platform/Menus/MenuController.cs
  51. 740
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/20220923113938_Add-User-Favorite-Menu.Designer.cs
  52. 53
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/20220923113938_Add-User-Favorite-Menu.cs
  53. 69
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/PlatformHttpApiHostMigrationsDbContextModelSnapshot.cs
  54. 11
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs
  55. 1
      aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json

75
apps/vue/src/api/weather/index.ts

@ -0,0 +1,75 @@
/*
数据来源: http://www.nmc.cn (中央气象台)
*/
import { defHttp } from '/@/utils/http/axios';
import { Position, Province, WeatherResult } from './model';
import { format } from '/@/utils/strings';
//const Host = 'http://www.nmc.cn';
const Api = {
GetProvinces: '/wapi/rest/province/all',
GetPosition: '/wapi/rest/position',
GetCitys: '/wapi/rest/province/{province}',
GetWeather: '/wapi/rest/weather?stationid={code}',
};
export const getProvinces = () => {
return defHttp.get<Province[]>({
url: Api.GetProvinces,
//baseURL: Host,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
}, {
apiUrl: '',
joinTime: false,
withToken: false,
withAcceptLanguage: false,
});
};
export const getPosition = () => {
return defHttp.get<Position>({
url: Api.GetPosition,
//baseURL: Host,
headers: {
'X-Requested-With': 'XMLHttpRequest',
}
}, {
apiUrl: '',
joinTime: false,
withToken: false,
withAcceptLanguage: false,
});
}
export const getCitys = (provinceCode: string) => {
return defHttp.get<Position[]>({
url: format(Api.GetCitys, {province: provinceCode}),
//baseURL: Host,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
}, {
apiUrl: '',
joinTime: false,
withToken: false,
withAcceptLanguage: false,
});
}
export const getWeather = (cityCode: string) => {
return defHttp.get<WeatherResult>({
url: format(Api.GetWeather, {code: cityCode}),
//baseURL: Host,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
}, {
apiUrl: '',
joinTime: false,
withToken: false,
withAcceptLanguage: false,
});
}

101
apps/vue/src/api/weather/model/index.ts

@ -0,0 +1,101 @@
/*
数据来源: http://www.nmc.cn (中央气象台)
*/
/**
*
*/
export interface Position {
/** 城市 */
city: string;
/** 代码 */
code: string;
/** 省份 */
province: string;
/** 专用页面 */
url: string;
}
/**
*
*/
export interface Province {
/** 代码 */
code: string;
/** 名称 */
name: string;
/** 专用页面 */
url: string;
}
export interface Air {
aq: number;
aqi: number;
aqiCode: string;
forecasttime: Date;
text: string;
}
export interface Weather {
airpressure: number;
feelst: number;
humidity: number;
icomfort: number;
img: string;
info: string;
rain: number;
rcomfort: number;
temperature: number;
temperatureDiff: number;
}
export interface Wind {
degree: number;
direct: string;
power: string;
speed: number;
}
export interface PredictDetail {
date: Date;
pt: Date;
day: { weather: Weather; wind: Wind };
night: { weather: Weather; wind: Wind };
}
export interface Predict {
detail: PredictDetail[];
publish_time: Date;
station: Position;
}
export interface Real {
publish_time: Date;
station: Position;
weather: Weather;
wind: Wind;
}
export interface Tempchart {
day_img: string;
day_text: string;
max_temp: number;
min_temp: number;
night_img: string;
night_text: string;
time: Date;
}
export interface WeatherInfo {
air: Air;
predict: Predict;
real: Real;
tempchart: Tempchart;
}
export interface WeatherResult {
code: number;
msg: string;
data: WeatherInfo;
}

6
apps/vue/src/components/Form/src/components/ApiTree.vue

@ -1,12 +1,12 @@
<template>
<a-tree v-bind="getAttrs" @change="handleChange">
<Tree v-bind="getAttrs" @change="handleChange">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
</a-tree>
</Tree>
</template>
<script lang="ts">
@ -18,7 +18,7 @@
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ApiTree',
components: { ATree: Tree, LoadingOutlined },
components: { Tree, LoadingOutlined },
props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object },

86
apps/vue/src/layouts/default/header/components/lock/LockModal.vue

@ -24,8 +24,8 @@
</div>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
<script lang="ts" setup>
import { computed } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
import { BasicModal, useModalInner } from '/@/components/Modal/index';
@ -34,61 +34,45 @@
import { useUserStore } from '/@/store/modules/user';
import { useLockStore } from '/@/store/modules/lock';
import headerImg from '/@/assets/images/header.jpg';
export default defineComponent({
name: 'LockModal',
components: { BasicModal, BasicForm },
setup() {
const { t } = useI18n();
const { prefixCls } = useDesign('header-lock-modal');
const userStore = useUserStore();
const lockStore = useLockStore();
const { t } = useI18n();
const { prefixCls } = useDesign('header-lock-modal');
const userStore = useUserStore();
const lockStore = useLockStore();
const getRealName = computed(() => userStore.getUserInfo?.realName);
const [register, { closeModal }] = useModalInner();
const getRealName = computed(() => userStore.getUserInfo?.realName);
const [register, { closeModal }] = useModalInner();
const [registerForm, { validateFields, resetFields }] = useForm({
showActionButtonGroup: false,
schemas: [
{
field: 'password',
label: t('layout.header.lockScreenPassword'),
colProps: {
span: 24,
},
component: 'InputPassword',
required: true,
},
],
});
async function handleLock() {
const values = (await validateFields()) as any;
const password: string | undefined = values.password;
closeModal();
const [registerForm, { validateFields, resetFields }] = useForm({
showActionButtonGroup: false,
schemas: [
{
field: 'password',
label: t('layout.header.lockScreenPassword'),
colProps: {
span: 24,
},
component: 'InputPassword',
required: true,
},
],
});
lockStore.setLockInfo({
isLock: true,
pwd: password,
});
await resetFields();
}
async function handleLock() {
const values = (await validateFields()) as any;
const password: string | undefined = values.password;
closeModal();
const avatar = computed(() => {
const { avatar } = userStore.getUserInfo;
return avatar || headerImg;
});
lockStore.setLockInfo({
isLock: true,
pwd: password,
});
await resetFields();
}
return {
t,
prefixCls,
getRealName,
register,
registerForm,
handleLock,
avatar,
};
},
const avatar = computed(() => {
const { avatar } = userStore.getUserInfo;
return avatar || headerImg;
});
</script>
<style lang="less">

21
apps/vue/src/locales/lang/en/routes/dashboard.ts

@ -1,6 +1,25 @@
export default {
dashboard: 'Dashboard',
about: 'About',
workbench: 'Workbench',
workbench: {
title: 'Workbench',
header: {
welcomeMorning: 'Good morning, {0}. Begin your day~',
welcomeAtoon: 'Good afternoon, {0}, pay attention to rest oh~',
welcomeAfternoon: 'Good afternoon, {0}, relax in time, can improve work efficiency~',
welcomeEvening: 'Good evening, {0}. Still at work? The off work~',
todayWeather: '{city} today {weather}, {temperature} ℃, {wind}, speed {speed}',
notifier: {
title: 'notifier',
count: '({0})',
}
},
menus: {
more: 'More',
addMenu: 'Add new menu',
manager: 'Manage menu',
selectMenu: '选择菜单'
}
},
analysis: 'Analysis',
};

21
apps/vue/src/locales/lang/zh-CN/routes/dashboard.ts

@ -1,6 +1,25 @@
export default {
dashboard: 'Dashboard',
about: '关于',
workbench: '工作台',
workbench: {
title: '工作台',
header: {
welcomeMorning: '早安, {0}, 开始您一天的工作吧~',
welcomeAtoon: '中午好, {0}, 注意休息哦~',
welcomeAfternoon: '下午好, {0}, 适时放松,可以提高工作效率~',
welcomeEvening: '晚上好, {0}, 还在工作么?该下班了~',
todayWeather: '{city} 今日 {weather}, {temperature} ℃, {wind}, 风速 {speed}',
notifier: {
title: '通知',
count: '({0})',
}
},
menus: {
more: '更多',
addMenu: '添加菜单',
manager: '管理菜单',
selectMenu: '选择菜单'
}
},
analysis: '分析页',
};

2
apps/vue/src/router/routes/modules/dashboard.ts

@ -28,7 +28,7 @@ const dashboard: AppRouteModule = {
name: 'Workbench',
component: () => import('/@/views/dashboard/workbench/index.vue'),
meta: {
title: t('routes.dashboard.workbench'),
title: t('routes.dashboard.workbench.title'),
},
},
],

36
apps/vue/src/store/modules/weather.ts

@ -0,0 +1,36 @@
import { defineStore } from 'pinia';
import { Position, WeatherInfo } from '/@/api/weather/model';
import { getPosition, getWeather } from '/@/api/weather';
interface WeatherState {
currentCity?: Position;
weather?: WeatherInfo;
lastUpdateHour: number;
}
export const useWeatherStore = defineStore({
id: 'weather',
state: (): WeatherState => ({
currentCity: undefined,
weather: undefined,
lastUpdateHour: -1,
}),
actions: {
async updateWeather() {
const hour = new Date().getHours();
if (hour - this.lastUpdateHour != 0) {
const position = await getPosition();
const weatherResult = await getWeather(position.code);
if (weatherResult.code !== 0) {
return Promise.reject(weatherResult.msg);
}
this.currentCity = position;
this.weather = weatherResult.data;
this.lastUpdateHour = hour;
return Promise.resolve(this.weather);
} else {
return Promise.resolve(this.weather);
}
}
}
});

11
apps/vue/src/utils/http/axios/index.ts

@ -123,6 +123,7 @@ const transform: AxiosTransform = {
* @description:
*/
requestInterceptors: (config, options) => {
config.headers = config.headers ?? {};
// 请求之前处理config
const token = getToken();
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
@ -131,10 +132,10 @@ const transform: AxiosTransform = {
? `${options.authenticationScheme} ${token}`
: token;
}
config.headers = config.headers ?? {};
const localeStore = useLocaleStoreWithOut();
config.headers['Accept-Language'] = localeStore.getLocale;
if ((config as Recordable)?.requestOptions?.withAcceptLanguage !== false) {
const localeStore = useLocaleStoreWithOut();
config.headers['Accept-Language'] = localeStore.getLocale;
}
return config;
},
@ -202,6 +203,8 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
ignoreCancelToken: true,
// 是否携带token
withToken: true,
// 是否携带 Accept-Language 标头
withAcceptLanguage: true,
retryRequest: {
isOpenRetry: true,
count: 5,

76
apps/vue/src/views/dashboard/workbench/components/MenuCard.vue

@ -0,0 +1,76 @@
<template>
<div>
<Card :title="title" class="menu-card">
<template #extra>
<Button type="link" @click="handleManageMenu">{{ t('routes.dashboard.workbench.menus.more') }}</Button>
</template>
<CardGrid v-for="menu in menus" :key="menu.title" class="menu-card-grid">
<span class="flex">
<Icon :icon="menu.icon" :color="menu.color" :size="menu.size ?? 30" />
<span class="text-lg ml-4">{{ menu.title }}</span>
</span>
<div v-if="menu.desc" class="flex mt-2 h-10 text-secondary">{{ menu.desc }}</div>
</CardGrid>
<CardGrid class="menu-card-grid" @click="handleAddNew">
<span class="flex">
<Icon icon="ion:add-outline" color="#00BFFF" :size="30" />
<span class="text-lg ml-4">{{ t('routes.dashboard.workbench.menus.addMenu') }}</span>
</span>
</CardGrid>
</Card>
<MenuReference @register="registerReference" @change="handleChange" />
</div>
</template>
<script lang="ts" setup>
import { Button, Card } from 'ant-design-vue';
import { Icon } from '/@/components/Icon';
import { useI18n } from '/@/hooks/web/useI18n';
import { useModal } from '/@/components/Modal';
import MenuReference from './MenuReference.vue';
const CardGrid = Card.Grid;
interface Menu {
title: string;
desc?: string;
icon?: string;
color?: string;
size?: number;
}
const emits = defineEmits(['change', 'register']);
const props = defineProps({
title: {
type: String,
required: true,
},
menus: {
type: Array as PropType<Menu[]>,
default: [],
}
});
const { t } = useI18n();
const [registerReference, { openModal: openReferenceModal }] = useModal();
function handleManageMenu() {
openReferenceModal(true, { defaultCheckedKeys: props.menus });
}
function handleAddNew() {
openReferenceModal(true, { radio: true });
}
function handleChange(menus) {
console.log(menus);
emits('change', menus);
}
</script>
<style lang="less" scoped>
.menu-card-grid {
margin: 10px;
width: 30%;
cursor: pointer;
}
</style>

86
apps/vue/src/views/dashboard/workbench/components/MenuReference.vue

@ -0,0 +1,86 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:title="t('routes.dashboard.workbench.menus.manager')"
:width="500"
@ok="handleSubmit"
>
<Form :model="formModel" ref="formElRef">
<Form.Item :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }" :label="t('routes.dashboard.workbench.menus.selectMenu')" name="menus">
<ATree
checkable
checkStrictly
:tree-data="menuTreeData"
:fieldNames="{
title: 'displayName',
key: 'id',
}"
v-model:checkedKeys="formModel.menus"
:selectedKeys="defaultCheckedKeys"
@check="handleMenuCheck" />
</Form.Item>
</Form>
</BasicModal>
</template>
<script lang="ts" setup>
import { reactive, ref, unref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { Form, Tree } from 'ant-design-vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { getMenuList } from '/@/api/sys/menu';
import { listToTree } from '/@/utils/helper/treeHelper';
const ATree = Tree;
const emits = defineEmits(['change', 'change:keys', 'register']);
const { t } = useI18n();
const formModel = reactive({
menus: [] as string[],
});
const formElRef = ref<any>(null);
const menuTreeData = ref<any[]>([]);
const radio = ref(false);
const defaultCheckedKeys = ref<string[]>([]);
const checkedMenus = ref<any[]>([]);
const [registerModal, { closeModal }] = useModalInner((props) => {
init(props);
fetchMyMenus();
});
function init(props) {
formModel.menus = [];
checkedMenus.value = [];
radio.value = props.radio ?? false;
defaultCheckedKeys.value = props.defaultCheckedKeys ?? [];
}
function fetchMyMenus() {
getMenuList().then((res) => {
const treeData = listToTree(res.items, { id: 'id', pid: 'parentId' });
menuTreeData.value = treeData;
})
}
function handleSubmit() {
const formEl = unref(formElRef);
formEl?.validate().then(() => {
emits('change:keys', formModel.menus);
emits('change', checkedMenus.value);
closeModal();
});
}
function handleMenuCheck(checkedKeys, e) {
if (checkedKeys.checked.length > 1 && radio.value) {
const menu = checkedKeys.checked.pop();
formModel.menus = [menu!];
}
checkedMenus.value = e.checkedNodes;
}
</script>
<style lang="less" scoped>
</style>

2
apps/vue/src/views/dashboard/workbench/components/ProjectCard.vue

@ -4,7 +4,7 @@
<a-button type="link" size="small">更多</a-button>
</template>
<CardGrid v-for="item in items" :key="item" class="!md:w-1/3 !w-full">
<CardGrid v-for="item in items" :key="item.group" class="!md:w-1/3 !w-full">
<span class="flex">
<Icon :icon="item.icon" :color="item.color" size="30" />
<span class="text-lg ml-4">{{ item.title }}</span>

84
apps/vue/src/views/dashboard/workbench/components/WorkbenchHeader.vue

@ -2,32 +2,88 @@
<div class="lg:flex">
<Avatar :src="userinfo.avatar || headerImg" :size="72" class="!mx-auto !block" />
<div class="md:ml-6 flex flex-col justify-center md:mt-0 mt-2">
<h1 class="md:text-lg text-md">早安, {{ userinfo.realName }}, 开始您一天的工作吧</h1>
<span class="text-secondary"> 今日晴20 - 32 </span>
<h1 class="md:text-lg text-md">{{ getWelcomeTitle }}</h1>
<span class="text-secondary">{{ getWeatherInfo }}</span>
</div>
<div class="flex flex-1 justify-end md:mt-0 mt-4">
<div class="flex flex-col justify-center text-right">
<span class="text-secondary"> 待办 </span>
<span class="text-2xl">2/10</span>
</div>
<div class="flex flex-col justify-center text-right md:mx-16 mx-12">
<span class="text-secondary"> 项目 </span>
<span class="text-2xl">8</span>
</div>
<div class="flex flex-col justify-center text-right md:mr-10 mr-4">
<span class="text-secondary"> 团队 </span>
<span class="text-2xl">300</span>
<span class="text-secondary">{{ t('routes.dashboard.workbench.header.notifier.title') }}</span>
<a href="javascript:void(0)" class="text-2xl">{{ t('routes.dashboard.workbench.header.notifier.count', [unReadNotiferCount]) }}</a>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { Avatar } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useUserStore } from '/@/store/modules/user';
import { useWeatherStore } from '/@/store/modules/weather';
import { getList as getNotifers } from '/@/api/messages/notifications';
import { NotificationReadState } from '/@/api/messages/model/notificationsModel';
import headerImg from '/@/assets/images/header.jpg';
const { t } = useI18n();
const userStore = useUserStore();
const weatherStore = useWeatherStore();
const userinfo = computed(() => userStore.getUserInfo);
const getUserInfo = computed(() => {
const { avatar, desc, realName, username } = userStore.getUserInfo || {};
let userName = realName ?? username;
return {
realName: userName,
avatar: avatar || headerImg,
desc
};
});
const getWelcomeTitle = computed(() => {
const now = new Date();
const hour = now.getHours();
if (hour < 12) {
return t('routes.dashboard.workbench.header.welcomeMorning', [getUserInfo.value.realName]);
}
if (hour < 14) {
return t('routes.dashboard.workbench.header.welcomeAtoon', [getUserInfo.value.realName]);
}
if (hour < 17) {
return t('routes.dashboard.workbench.header.welcomeAfternoon', [getUserInfo.value.realName]);
}
if (hour < 24) {
return t('routes.dashboard.workbench.header.welcomeEvening', [getUserInfo.value.realName]);
}
});
const getWeatherInfo = computed(() => {
const weather = weatherStore.weather;
if (!weather) {
return '';
}
return t('routes.dashboard.workbench.header.todayWeather', {
city: weather.real.station.city,
weather: weather.real.weather.info,
temperature: weather.real.weather.temperature,
wind: `${weather.real.wind.direct} - ${weather.real.wind.power}`,
speed: weather.real.wind.speed,
});
});
const unReadNotiferCount = ref(0);
onMounted(() => {
fetchUnReadNotifiers();
weatherStore.updateWeather();
});
function fetchUnReadNotifiers() {
getNotifers({
skipCount: 0,
maxResultCount: 1,
reverse: false,
sorting: undefined,
readState: NotificationReadState.UnRead,
}).then((res) => {
unReadNotiferCount.value = res.totalCount;
});
}
</script>

47
apps/vue/src/views/dashboard/workbench/index.vue

@ -3,30 +3,59 @@
<template #headerContent> <WorkbenchHeader /> </template>
<div class="lg:flex">
<div class="lg:w-7/10 w-full !mr-4 enter-y">
<ProjectCard :loading="loading" class="enter-y" />
<DynamicInfo :loading="loading" class="!my-4 enter-y" />
<MenuCard class="enter-y" title="常用" :menus="usedMenus" />
</div>
<div class="lg:w-3/10 w-full enter-y">
<QuickNav :loading="loading" class="enter-y" />
<!-- <QuickNav :loading="loading" class="enter-y" /> -->
<Card class="!my-4 enter-y" :loading="loading">
<img class="xl:h-50 h-30 mx-auto" src="../../../assets/svg/illustration.svg" />
</Card>
<SaleRadar :loading="loading" class="enter-y" />
<!-- <SaleRadar :loading="loading" class="enter-y" /> -->
</div>
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { reactive, ref } from 'vue';
import { Card } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
import WorkbenchHeader from './components/WorkbenchHeader.vue';
import ProjectCard from './components/ProjectCard.vue';
import QuickNav from './components/QuickNav.vue';
import DynamicInfo from './components/DynamicInfo.vue';
import SaleRadar from './components/SaleRadar.vue';
import MenuCard from './components/MenuCard.vue';
const usedMenus = reactive([
{
title: '首页',
icon: 'ion:home-outline',
color: '#1fdaca',
},
{
title: '仪表盘',
icon: 'ion:grid-outline',
color: '#bf0c2c',
},
{
title: '组件',
icon: 'ion:layers-outline',
color: '#e18525',
},
{
title: '系统管理',
icon: 'ion:settings-outline',
color: '#3fb27f',
},
{
title: '权限管理',
icon: 'ion:key-outline',
color: '#4daf1bc9',
},
{
title: '图表',
icon: 'ion:bar-chart-outline',
color: '#00d8ff',
},
]);
const loading = ref(true);

2
apps/vue/types/axios.d.ts

@ -23,6 +23,8 @@ export interface RequestOptions {
ignoreCancelToken?: boolean;
// Whether to send token in header
withToken?: boolean;
// Whether to send Accept-Language in header
withAcceptLanguage?: boolean;
}
export interface Result<T = any> {

22
apps/vue/vite.config.ts

@ -1,7 +1,7 @@
import type { UserConfig, ConfigEnv } from 'vite';
import pkg from './package.json';
import dayjs from 'dayjs';
import { loadEnv } from 'vite';
import { loadEnv, ProxyOptions } from 'vite';
import { resolve } from 'path';
import { generateModifyVars } from './build/generate/generateModifyVars';
import { createProxy } from './build/vite/proxy';
@ -9,6 +9,24 @@ import { wrapperEnv } from './build/utils';
import { createVitePlugins } from './build/vite/plugin';
import { OUTPUT_DIR } from './build/constant';
/**
* api代理
* production环境请在nginx配置
*/
function buildWeatherProxy(): Record<string, ProxyOptions> {
return {
'/wapi': {
target: 'http://www.nmc.cn',
changeOrigin: true,
rewrite: (path) => path.replace('/wapi', ''),
headers: {
host: 'www.nmc.cn',
referer: 'http://www.nmc.cn',
},
},
};
}
function pathResolve(dir: string) {
return resolve(process.cwd(), '.', dir);
}
@ -58,7 +76,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
host: true,
port: VITE_PORT,
// Load proxy configuration from .env
proxy: createProxy(VITE_PROXY),
proxy: { ...createProxy(VITE_PROXY), ...buildWeatherProxy() },
},
esbuild: {
pure: VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : [],

28
aspnet-core/modules/platform/LINGYUN.Abp.UI.Navigation.VueVbenAdmin/LINGYUN/Abp/UI/Navigation/VueVbenAdmin/VueVbenAdminStandardMenuConverter.cs

@ -0,0 +1,28 @@
using LINGYUN.Platform.Menus;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.UI.Navigation.VueVbenAdmin;
[Dependency(ReplaceServices = true)]
public class VueVbenAdminStandardMenuConverter : IStandardMenuConverter, ISingletonDependency
{
public StandardMenu Convert(Menu menu)
{
var standardMenu = new StandardMenu
{
Icon = "",
Name = menu.Name,
Path = menu.Path,
DisplayName = menu.DisplayName,
Description = menu.Description,
Redirect = menu.Redirect,
};
if (menu.ExtraProperties.TryGetValue("icon", out var icon))
{
standardMenu.Icon = icon.ToString();
}
return standardMenu;
}
}

3
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/en.json

@ -27,6 +27,7 @@
"Permission:Delete": "Delete",
"Permission:ManageItems": "Manage Items",
"Permission:ManageRoleMenus": "Manage Role Menus",
"Permission:ManageUserMenus": "Manage User Menus"
"Permission:ManageUserMenus": "Manage User Menus",
"Permission:ManageUserFavoriteMenus": "Manage User Favorite Menus"
}
}

3
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Localization/ApplicationContracts/zh-Hans.json

@ -27,6 +27,7 @@
"Permission:Delete": "删除",
"Permission:ManageItems": "管理项目",
"Permission:ManageRoleMenus": "管理角色菜单",
"Permission:ManageUserMenus": "管理用户菜单"
"Permission:ManageUserMenus": "管理用户菜单",
"Permission:ManageUserFavoriteMenus": "管理用户收藏菜单"
}
}

21
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuDto.cs

@ -0,0 +1,21 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Platform.Menus;
public class UserFavoriteMenuDto : AuditedEntityDto<Guid>
{
public Guid MenuId { get; set; }
public Guid UserId { get; set; }
public string Framework { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public string Path { get; set; }
public string Icon { get; set; }
}

9
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuGetListInput.cs

@ -0,0 +1,9 @@
using LINGYUN.Platform.Routes;
using Volo.Abp.Validation;
namespace LINGYUN.Platform.Menus;
public class UserFavoriteMenuGetListInput
{
[DynamicStringLength(typeof(LayoutConsts), nameof(LayoutConsts.MaxFrameworkLength))]
public string Framework { get; set; }
}

11
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/Dto/UserFavoriteMenuSetInput.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Platform.Menus;
public class UserFavoriteMenuSetInput
{
public string Framework { get; set; }
public List<Guid> MenuIds { get; set; } = new List<Guid>();
}

8
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Menus/IMenuAppService.cs

@ -23,10 +23,18 @@ namespace LINGYUN.Platform.Menus
Task SetUserStartupAsync(Guid id, UserMenuStartupInput input);
Task SetUserFavoriteMenusAsync(Guid userId, UserFavoriteMenuSetInput input);
Task SetRoleMenusAsync(RoleMenuInput input);
Task SetRoleStartupAsync(Guid id, RoleMenuStartupInput input);
Task<ListResultDto<MenuDto>> GetCurrentUserMenuListAsync(GetMenuInput input);
Task SetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuSetInput input);
Task<ListResultDto<UserFavoriteMenuDto>> GetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuGetListInput input);
Task<ListResultDto<UserFavoriteMenuDto>> GetUserFavoriteMenuListAsync(Guid userId, UserFavoriteMenuGetListInput input);
}
}

3
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissionDefinitionProvider.cs

@ -36,7 +36,8 @@ namespace LINGYUN.Platform.Permissions
menu.AddChild(PlatformPermissions.Menu.Update, L("Permission:Update"));
menu.AddChild(PlatformPermissions.Menu.Delete, L("Permission:Delete"));
menu.AddChild(PlatformPermissions.Menu.ManageRoles, L("Permission:ManageRoleMenus"));
menu.AddChild(PlatformPermissions.Menu.ManageUsers, L("Permission:ManageUserMenus"));
menu.AddChild(PlatformPermissions.Menu.ManageUsers, L("Permission:ManageUserMenus"));
menu.AddChild(PlatformPermissions.Menu.ManageUserFavorites, L("Permission:ManageUserFavoriteMenus"));
// TODO: 2020-07-27 目前abp不支持对象存储管理(或者属于企业版?)需要创建一个 LINGYUN.Abp.BlobStoring 项目自行实现

4
aspnet-core/modules/platform/LINGYUN.Platform.Application.Contracts/LINGYUN/Platform/Permissions/PlatformPermissions.cs

@ -44,7 +44,9 @@ namespace LINGYUN.Platform.Permissions
public const string ManageRoles = Default + ".ManageRoles";
public const string ManageUsers = Default + ".ManageUsers";
public const string ManageUsers = Default + ".ManageUsers";
public const string ManageUserFavorites = Default + ".ManageUserFavorites";
}
// 如果abp后期提供对象存储的目录管理接口,则启用此权限

73
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/Menus/MenuAppService.cs

@ -17,10 +17,13 @@ namespace LINGYUN.Platform.Menus
[Authorize]
public class MenuAppService : PlatformApplicationServiceBase, IMenuAppService
{
protected IStandardMenuConverter StandardMenuConverter => LazyServiceProvider.LazyGetRequiredService<IStandardMenuConverter>();
protected DataItemMappingOptions DataItemMapping { get; }
protected MenuManager MenuManager { get; }
protected IMenuRepository MenuRepository { get; }
protected IUserMenuRepository UserMenuRepository { get; }
protected IUserFavoriteMenuRepository UserFavoriteMenuRepository { get; }
protected IRoleMenuRepository RoleMenuRepository { get; }
protected IDataRepository DataRepository { get; }
protected ILayoutRepository LayoutRepository { get; }
@ -32,6 +35,7 @@ namespace LINGYUN.Platform.Menus
ILayoutRepository layoutRepository,
IUserMenuRepository userMenuRepository,
IRoleMenuRepository roleMenuRepository,
IUserFavoriteMenuRepository userFavoriteMenuRepository,
IOptions<DataItemMappingOptions> options)
{
MenuManager = menuManager;
@ -40,6 +44,7 @@ namespace LINGYUN.Platform.Menus
LayoutRepository = layoutRepository;
UserMenuRepository = userMenuRepository;
RoleMenuRepository = roleMenuRepository;
UserFavoriteMenuRepository = userFavoriteMenuRepository;
DataItemMapping = options.Value;
}
@ -305,5 +310,73 @@ namespace LINGYUN.Platform.Menus
return new ListResultDto<MenuDto>(menuDtos);
}
[Authorize(PlatformPermissions.Menu.ManageUserFavorites)]
public async virtual Task SetUserFavoriteMenusAsync(Guid userId, UserFavoriteMenuSetInput input)
{
await SetUserFavoriteMenuListAsync(userId, input);
}
public async virtual Task SetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuSetInput input)
{
await SetUserFavoriteMenuListAsync(CurrentUser.GetId(), input);
}
[Authorize(PlatformPermissions.Menu.ManageUserFavorites)]
public async virtual Task<ListResultDto<UserFavoriteMenuDto>> GetUserFavoriteMenuListAsync(Guid userId, UserFavoriteMenuGetListInput input)
{
var userFacoriteMenus = await UserFavoriteMenuRepository.GetFavoriteMenusAsync(
userId, input.Framework);
return new ListResultDto<UserFavoriteMenuDto>(
ObjectMapper.Map<List<UserFavoriteMenu>, List<UserFavoriteMenuDto>>(userFacoriteMenus));
}
public async virtual Task<ListResultDto<UserFavoriteMenuDto>> GetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuGetListInput input)
{
var userFacoriteMenus = await UserFavoriteMenuRepository.GetFavoriteMenusAsync(
CurrentUser.GetId(), input.Framework);
return new ListResultDto<UserFavoriteMenuDto>(
ObjectMapper.Map<List<UserFavoriteMenu>, List<UserFavoriteMenuDto>>(userFacoriteMenus));
}
protected async virtual Task SetUserFavoriteMenuListAsync(Guid userId, UserFavoriteMenuSetInput input)
{
var userFacoriteMenus = await UserFavoriteMenuRepository.GetFavoriteMenusAsync(
userId, input.Framework);
var wellDeleteMenus = userFacoriteMenus.Where(um => !input.MenuIds.Any(id => id == um.MenuId));
var wellInsertMenus = input.MenuIds.Where(id => !userFacoriteMenus.Any(um => um.MenuId == id));
if (wellInsertMenus.Any())
{
var insertMenus = new List<UserFavoriteMenu>();
var menus = await MenuRepository.GetListAsync(wellInsertMenus.Select(id => id));
foreach (var menu in menus)
{
var standardMenu = StandardMenuConverter.Convert(menu);
insertMenus.Add(
new UserFavoriteMenu(
GuidGenerator.Create(),
menu.Id,
userId,
menu.Framework,
standardMenu.Name,
standardMenu.DisplayName,
standardMenu.Path,
standardMenu.Icon,
CurrentTenant.Id));
}
await UserFavoriteMenuRepository.InsertManyAsync(insertMenus);
}
await UserFavoriteMenuRepository.DeleteManyAsync(wellDeleteMenus);
await CurrentUnitOfWork.SaveChangesAsync();
}
}
}

1
aspnet-core/modules/platform/LINGYUN.Platform.Application/LINGYUN/Platform/PlatformApplicationMappingProfile.cs

@ -20,6 +20,7 @@ namespace LINGYUN.Platform
.ForMember(dto => dto.Startup, map => map.Ignore());
CreateMap<Layout, LayoutDto>()
.ForMember(dto => dto.Meta, map => map.MapFrom(src => src.ExtraProperties));
CreateMap<UserFavoriteMenu, UserFavoriteMenuDto>();
}
}
}

4
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN.Platform.Domain.Shared.csproj

@ -22,8 +22,4 @@
<PackageReference Include="Volo.Abp.Validation" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<Folder Include="LINGYUN\Platform\Layouts\" />
</ItemGroup>
</Project>

10
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Layouts/LayoutEto.cs

@ -0,0 +1,10 @@
using LINGYUN.Platform.Routes;
using Volo.Abp.EventBus;
namespace LINGYUN.Platform.Layouts
{
[EventName("platform.layouts.layout")]
public class LayoutEto : RouteEto
{
}
}

11
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/MenuEto.cs

@ -0,0 +1,11 @@
using LINGYUN.Platform.Routes;
using Volo.Abp.EventBus;
namespace LINGYUN.Platform.Menus
{
[EventName("platform.menus.menu")]
public class MenuEto : RouteEto
{
public string Framework { get; set; }
}
}

14
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/RoleMenuEto.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.EventBus;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Menus
{
[EventName("platform.menus.role_menu")]
public class RoleMenuEto : IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid MenuId { get; set; }
public string RoleName { get; set; }
}
}

14
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Menus/UserMenuEto.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.EventBus;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Menus
{
[EventName("platform.menus.user_menu")]
public class UserMenuEto : IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid MenuId { get; set; }
public Guid UserId { get; set; }
}
}

12
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RoleRouteEto.cs

@ -1,12 +0,0 @@
using System;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Routes
{
public class RoleRouteEto : IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid RouteId { get; set; }
public string RoleName { get; set; }
}
}

2
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/RouteEto.cs

@ -3,7 +3,7 @@ using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Routes
{
public class RouteEto : IMultiTenant
public abstract class RouteEto : IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid Id { get; set; }

5
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/UserFavoriteMenuConsts.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Platform.Routes;
public class UserFavoriteMenuConsts
{
public static int MaxIconLength { get; set; } = 512;
}

12
aspnet-core/modules/platform/LINGYUN.Platform.Domain.Shared/LINGYUN/Platform/Routes/UserRouteEto.cs

@ -1,12 +0,0 @@
using System;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Routes
{
public class UserRouteEto : IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid RouteId { get; set; }
public Guid UserId { get; set; }
}
}

27
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Datas/DataItemMappingOptions.cs

@ -2,6 +2,7 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Volo.Abp;
namespace LINGYUN.Platform.Datas
@ -24,14 +25,34 @@ namespace LINGYUN.Platform.Datas
return "";
}
if (value is JArray array)
if (value is Array array)
{
var joinString = string.Empty;
foreach (var obj in array.Children())
foreach (var obj in array)
{
joinString += obj.ToString() + ",";
}
return joinString.EndsWith(",") ? joinString.Substring(0, joinString.Length - 1) : joinString;
return joinString.EndsWith(",") ? joinString[..^1] : joinString;
}
if (value is JsonArray jsonArray)
{
var joinString = string.Empty;
foreach (var node in jsonArray)
{
joinString += node.ToString() + ",";
}
return joinString.EndsWith(",") ? joinString[..^1] : joinString;
}
if (value is JArray jArray)
{
var joinString = string.Empty;
foreach (var token in jArray.Children())
{
joinString += token.ToString() + ",";
}
return joinString.EndsWith(",") ? joinString[..^1] : joinString;
}
throw new BusinessException(PlatformErrorCodes.MetaFormatMissMatch);
});

20
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/DefaultStandardMenuConverter.cs

@ -0,0 +1,20 @@
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Platform.Menus;
[Dependency(TryRegister = true)]
public class DefaultStandardMenuConverter : IStandardMenuConverter, ISingletonDependency
{
public StandardMenu Convert(Menu menu)
{
return new StandardMenu
{
Icon = "",
Name = menu.Name,
Path = menu.Path,
DisplayName = menu.DisplayName,
Description = menu.Description,
Redirect = menu.Redirect,
};
}
}

3
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IMenuRepository.cs

@ -8,6 +8,9 @@ namespace LINGYUN.Platform.Menus
{
public interface IMenuRepository : IBasicRepository<Menu, Guid>
{
Task<List<Menu>> GetListAsync(
IEnumerable<Guid> idList,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取最后一个菜单
/// </summary>

6
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IStandardMenuConverter.cs

@ -0,0 +1,6 @@
namespace LINGYUN.Platform.Menus;
public interface IStandardMenuConverter
{
StandardMenu Convert(Menu menu);
}

20
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/IUserFavoriteMenuRepository.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Platform.Menus;
public interface IUserFavoriteMenuRepository : IBasicRepository<UserFavoriteMenu, Guid>
{
Task<List<UserFavoriteMenu>> GetListByMenuIdAsync(
Guid menuId,
CancellationToken cancellationToken = default);
Task<List<UserFavoriteMenu>> GetFavoriteMenusAsync(
Guid userId,
string framework = null,
Guid? menuId = null,
CancellationToken cancellationToken = default);
}

71
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/MenuChangeHandler.cs

@ -0,0 +1,71 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
namespace LINGYUN.Platform.Menus;
public class MenuChangeHandler :
IDistributedEventHandler<EntityUpdatedEto<MenuEto>>,
IDistributedEventHandler<EntityDeletedEto<MenuEto>>,
IDistributedEventHandler<EntityDeletedEto<UserMenuEto>>,
ITransientDependency
{
private readonly IMenuRepository _menuRepository;
private readonly IStandardMenuConverter _standardMenuConverter;
private readonly IUserFavoriteMenuRepository _userFavoriteMenuRepository;
public MenuChangeHandler(
IMenuRepository menuRepository,
IStandardMenuConverter standardMenuConverter,
IUserFavoriteMenuRepository userFavoriteMenuRepository)
{
_menuRepository = menuRepository;
_standardMenuConverter = standardMenuConverter;
_userFavoriteMenuRepository = userFavoriteMenuRepository;
}
[UnitOfWork]
public async virtual Task HandleEventAsync(EntityUpdatedEto<MenuEto> eventData)
{
// 菜单变更同步变更收藏菜单
var menu = await _menuRepository.GetAsync(eventData.Entity.Id);
var favoriteMenus = await _userFavoriteMenuRepository.GetListByMenuIdAsync(menu.Id);
var standardMenu = _standardMenuConverter.Convert(menu);
foreach (var favoriteMenu in favoriteMenus)
{
favoriteMenu.Framework = menu.Framework;
favoriteMenu.Name = standardMenu.Name;
favoriteMenu.Path = standardMenu.Path;
favoriteMenu.Icon = standardMenu.Icon;
favoriteMenu.DisplayName = standardMenu.DisplayName;
}
await _userFavoriteMenuRepository.UpdateManyAsync(favoriteMenus);
}
[UnitOfWork]
public async virtual Task HandleEventAsync(EntityDeletedEto<MenuEto> eventData)
{
// 菜单删除同步删除收藏菜单
var favoriteMenus = await _userFavoriteMenuRepository.GetListByMenuIdAsync(eventData.Entity.Id);
await _userFavoriteMenuRepository.DeleteManyAsync(favoriteMenus);
}
[UnitOfWork]
public async virtual Task HandleEventAsync(EntityDeletedEto<UserMenuEto> eventData)
{
// 用户菜单删除同步删除收藏菜单
var favoriteMenus = await _userFavoriteMenuRepository.GetListByMenuIdAsync(
eventData.Entity.UserId);
await _userFavoriteMenuRepository.DeleteManyAsync(favoriteMenus);
}
}

10
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/StandardMenu.cs

@ -0,0 +1,10 @@
namespace LINGYUN.Platform.Menus;
public class StandardMenu
{
public string Icon { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string Redirect { get; set; }
}

47
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/Menus/UserFavoriteMenu.cs

@ -0,0 +1,47 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Platform.Menus;
public class UserFavoriteMenu : AuditedEntity<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid MenuId { get; protected set; }
public virtual Guid UserId { get; protected set; }
public virtual string Framework { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayName { get; set; }
public virtual string Path { get; set; }
public virtual string Icon { get; set; }
protected UserFavoriteMenu() { }
public UserFavoriteMenu(
Guid id,
Guid menuId,
Guid userId,
string framework,
string name,
string displayName,
string path,
string icon,
Guid? tenantId = null)
: base(id)
{
MenuId = menuId;
UserId = userId;
Framework = framework;
Name = name;
DisplayName = displayName;
Path = path;
Icon = icon;
TenantId = tenantId;
}
}

8
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainMappingProfile.cs

@ -1,4 +1,6 @@
using AutoMapper;
using LINGYUN.Platform.Layouts;
using LINGYUN.Platform.Menus;
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Versions;
@ -8,7 +10,11 @@ namespace LINGYUN.Platform
{
public PlatformDomainMappingProfile()
{
CreateMap<Route, RouteEto>();
CreateMap<Layout, LayoutEto>();
CreateMap<Menu, MenuEto>();
CreateMap<UserMenu, UserMenuEto>();
CreateMap<RoleMenu, RoleMenuEto>();
CreateMap<AppVersion, AppVersionEto>()
.ForMember(eto => eto.FileCount, map => map.MapFrom(src => src.Files.Count));

10
aspnet-core/modules/platform/LINGYUN.Platform.Domain/LINGYUN/Platform/PlatformDomainModule.cs

@ -1,4 +1,6 @@
using LINGYUN.Platform.Datas;
using LINGYUN.Platform.Layouts;
using LINGYUN.Platform.Menus;
using LINGYUN.Platform.ObjectExtending;
using LINGYUN.Platform.Routes;
using LINGYUN.Platform.Versions;
@ -42,11 +44,13 @@ namespace LINGYUN.Platform
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.EtoMappings.Add<Route, RouteEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<Layout, LayoutEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<AppVersion, AppVersionEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<Menu, MenuEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<UserMenu, UserMenuEto>(typeof(PlatformDomainModule));
options.EtoMappings.Add<RoleMenu, RoleMenuEto>(typeof(PlatformDomainModule));
options.AutoEventSelectors.Add<AppVersion>();
options.EtoMappings.Add<AppVersion, AppVersionEto>(typeof(PlatformDomainModule));
});
}
public override void PostConfigureServices(ServiceConfigurationContext context)

28
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/EntityFrameworkCore/PlatformDbContextModelBuilderExtensions.cs

@ -82,6 +82,34 @@ namespace LINGYUN.Platform.EntityFrameworkCore
x.HasIndex(i => new { i.UserId, i.MenuId });
});
builder.Entity<UserFavoriteMenu>(x =>
{
x.ToTable(options.TablePrefix + "UserFavoriteMenus");
x.Property(p => p.Framework)
.HasMaxLength(LayoutConsts.MaxFrameworkLength)
.HasColumnName(nameof(Menu.Framework))
.IsRequired();
x.Property(p => p.DisplayName)
.HasMaxLength(RouteConsts.MaxDisplayNameLength)
.HasColumnName(nameof(Route.DisplayName))
.IsRequired();
x.Property(p => p.Name)
.HasMaxLength(RouteConsts.MaxNameLength)
.HasColumnName(nameof(Route.Name))
.IsRequired();
x.Property(p => p.Path)
.HasMaxLength(RouteConsts.MaxPathLength)
.HasColumnName(nameof(Route.Path));
x.Property(p => p.Icon)
.HasMaxLength(UserFavoriteMenuConsts.MaxIconLength)
.HasColumnName(nameof(UserFavoriteMenu.Icon));
x.ConfigureByConvention();
x.HasIndex(i => new { i.UserId, i.MenuId });
});
builder.Entity<Data>(x =>
{
x.ToTable(options.TablePrefix + "Datas");

9
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreMenuRepository.cs

@ -19,6 +19,15 @@ namespace LINGYUN.Platform.Menus
{
}
public async virtual Task<List<Menu>> GetListAsync(
IEnumerable<Guid> idList,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(x => idList.Contains(x.Id))
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async virtual Task<Menu> GetLastMenuAsync(
Guid? parentId = null,
CancellationToken cancellationToken = default)

44
aspnet-core/modules/platform/LINGYUN.Platform.EntityFrameworkCore/LINGYUN/Platform/Menus/EfCoreUserFavoriteMenuRepository.cs

@ -0,0 +1,44 @@
using LINGYUN.Platform.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Platform.Menus;
public class EfCoreUserFavoriteMenuRepository :
EfCoreRepository<PlatformDbContext, UserFavoriteMenu, Guid>,
IUserFavoriteMenuRepository
{
public EfCoreUserFavoriteMenuRepository(
IDbContextProvider<PlatformDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async virtual Task<List<UserFavoriteMenu>> GetListByMenuIdAsync(
Guid menuId,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(x => x.MenuId == menuId)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async virtual Task<List<UserFavoriteMenu>> GetFavoriteMenusAsync(
Guid userId,
string framework = null,
Guid? menuId = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.Where(x => x.UserId == userId)
.WhereIf(menuId.HasValue, x => x.MenuId == menuId)
.WhereIf(!framework.IsNullOrWhiteSpace(), x => x.Framework == framework)
.OrderBy(x => x.CreationTime)
.ToListAsync(GetCancellationToken(cancellationToken));
}
}

28
aspnet-core/modules/platform/LINGYUN.Platform.HttpApi/LINGYUN/Platform/Menus/MenuController.cs

@ -138,5 +138,33 @@ namespace LINGYUN.Platform.Menus
Framework = framework
});
}
[HttpPut]
[Route("favorites/by-user/{userId}")]
public async virtual Task SetUserFavoriteMenusAsync(Guid userId, UserFavoriteMenuSetInput input)
{
await MenuAppService.SetUserFavoriteMenusAsync(userId, input);
}
[HttpGet]
[Route("favorites/by-user/{userId}")]
public async virtual Task<ListResultDto<UserFavoriteMenuDto>> GetUserFavoriteMenuListAsync(Guid userId, UserFavoriteMenuGetListInput input)
{
return await MenuAppService.GetUserFavoriteMenuListAsync(userId, input);
}
[HttpPut]
[Route("favorites/by-current-user")]
public async virtual Task SetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuSetInput input)
{
await MenuAppService.SetCurrentUserFavoriteMenuListAsync(input);
}
[HttpGet]
[Route("favorites/by-current-user")]
public async virtual Task<ListResultDto<UserFavoriteMenuDto>> GetCurrentUserFavoriteMenuListAsync(UserFavoriteMenuGetListInput input)
{
return await MenuAppService.GetCurrentUserFavoriteMenuListAsync(input);
}
}
}

740
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/20220923113938_Add-User-Favorite-Menu.Designer.cs

@ -0,0 +1,740 @@
// <auto-generated />
using System;
using LY.MicroService.PlatformManagement.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace LY.MicroService.PlatformManagement.Migrations
{
[DbContext(typeof(PlatformManagementMigrationsDbContext))]
[Migration("20220923113938_Add-User-Favorite-Menu")]
partial class AddUserFavoriteMenu
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql)
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("varchar(1024)")
.HasColumnName("Code");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("char(36)")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("varchar(1024)")
.HasColumnName("Description");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsStatic")
.HasColumnType("tinyint(1)");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("varchar(30)")
.HasColumnName("Name");
b.Property<Guid?>("ParentId")
.HasColumnType("char(36)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("Name");
b.ToTable("AppPlatformDatas", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<bool>("AllowBeNull")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(true);
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<Guid>("DataId")
.HasColumnType("char(36)");
b.Property<string>("DefaultValue")
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DefaultValue");
b.Property<Guid?>("DeleterId")
.HasColumnType("char(36)")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("varchar(1024)")
.HasColumnName("Description");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsStatic")
.HasColumnType("tinyint(1)");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("varchar(30)")
.HasColumnName("Name");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<int>("ValueType")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("DataId");
b.HasIndex("Name");
b.ToTable("AppPlatformDataItems", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Layouts.Layout", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<Guid>("DataId")
.HasColumnType("char(36)");
b.Property<Guid?>("DeleterId")
.HasColumnType("char(36)")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<string>("Framework")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Framework");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Name");
b.Property<string>("Path")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Path");
b.Property<string>("Redirect")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Redirect");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("AppPlatformLayouts", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.Menu", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(23)
.HasColumnType("varchar(23)")
.HasColumnName("Code");
b.Property<string>("Component")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Component");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("char(36)")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<string>("Framework")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Framework");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsPublic")
.HasColumnType("tinyint(1)");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<Guid>("LayoutId")
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Name");
b.Property<Guid?>("ParentId")
.HasColumnType("char(36)");
b.Property<string>("Path")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Path");
b.Property<string>("Redirect")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Redirect");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("AppPlatformMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.RoleMenu", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<Guid>("MenuId")
.HasColumnType("char(36)");
b.Property<string>("RoleName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasColumnName("RoleName");
b.Property<bool>("Startup")
.HasColumnType("tinyint(1)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("RoleName", "MenuId");
b.ToTable("AppPlatformRoleMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("Framework")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Framework");
b.Property<string>("Icon")
.HasMaxLength(512)
.HasColumnType("varchar(512)")
.HasColumnName("Icon");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<Guid>("MenuId")
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Name");
b.Property<string>("Path")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Path");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("UserId", "MenuId");
b.ToTable("AppPlatformUserFavoriteMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<Guid>("MenuId")
.HasColumnType("char(36)");
b.Property<bool>("Startup")
.HasColumnType("tinyint(1)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("UserId", "MenuId");
b.ToTable("AppPlatformUserMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Versions.AppVersion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("char(36)")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(2048)
.HasColumnType("varchar(2048)")
.HasColumnName("Description");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<int>("Level")
.HasColumnType("int");
b.Property<int>("PlatformType")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasColumnName("Title");
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)")
.HasColumnName("Version");
b.HasKey("Id");
b.HasIndex("Version");
b.ToTable("AppPlatformVersion", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Versions.VersionFile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<Guid>("AppVersionId")
.HasColumnType("char(36)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<int>("DownloadCount")
.HasColumnType("int");
b.Property<int>("FileType")
.HasColumnType("int");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Name");
b.Property<string>("Path")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Path");
b.Property<string>("SHA256")
.IsRequired()
.HasMaxLength(65)
.HasColumnType("varchar(65)")
.HasColumnName("SHA256");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)")
.HasColumnName("Version");
b.HasKey("Id");
b.HasIndex("AppVersionId");
b.HasIndex("Path", "Name", "Version")
.IsUnique();
b.ToTable("AppPlatformVersionFile", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Datas.DataItem", b =>
{
b.HasOne("LINGYUN.Platform.Datas.Data", null)
.WithMany("Items")
.HasForeignKey("DataId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("LINGYUN.Platform.Versions.VersionFile", b =>
{
b.HasOne("LINGYUN.Platform.Versions.AppVersion", "AppVersion")
.WithMany("Files")
.HasForeignKey("AppVersionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AppVersion");
});
modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("LINGYUN.Platform.Versions.AppVersion", b =>
{
b.Navigation("Files");
});
#pragma warning restore 612, 618
}
}
}

53
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/20220923113938_Add-User-Favorite-Menu.cs

@ -0,0 +1,53 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LY.MicroService.PlatformManagement.Migrations
{
public partial class AddUserFavoriteMenu : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AppPlatformUserFavoriteMenus",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
MenuId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UserId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Framework = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Name = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DisplayName = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Path = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Icon = table.Column<string>(type: "varchar(512)", maxLength: 512, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
LastModificationTime = table.Column<DateTime>(type: "datetime(6)", nullable: true),
LastModifierId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_AppPlatformUserFavoriteMenus", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_AppPlatformUserFavoriteMenus_UserId_MenuId",
table: "AppPlatformUserFavoriteMenus",
columns: new[] { "UserId", "MenuId" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AppPlatformUserFavoriteMenus");
}
}
}

69
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/Migrations/PlatformHttpApiHostMigrationsDbContextModelSnapshot.cs

@ -18,7 +18,7 @@ namespace LY.MicroService.PlatformManagement.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql)
.HasAnnotation("ProductVersion", "6.0.2")
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("LINGYUN.Platform.Datas.Data", b =>
@ -443,6 +443,73 @@ namespace LY.MicroService.PlatformManagement.Migrations
b.ToTable("AppPlatformRoleMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.UserFavoriteMenu", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)")
.HasColumnName("DisplayName");
b.Property<string>("Framework")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Framework");
b.Property<string>("Icon")
.HasMaxLength(512)
.HasColumnType("varchar(512)")
.HasColumnName("Icon");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<Guid>("MenuId")
.HasColumnType("char(36)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)")
.HasColumnName("Name");
b.Property<string>("Path")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Path");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("UserId", "MenuId");
b.ToTable("AppPlatformUserFavoriteMenus", (string)null);
});
modelBuilder.Entity("LINGYUN.Platform.Menus.UserMenu", b =>
{
b.Property<Guid>("Id")

11
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/PlatformManagementHttpApiHostModule.Configure.cs

@ -38,7 +38,7 @@ namespace LY.MicroService.PlatformManagement;
public partial class PlatformManagementHttpApiHostModule
{
protected const string DefaultCorsPolicyName = "Default";
protected const string ApplicationName = "Platform";
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
private void PreConfigureFeature()
@ -51,7 +51,7 @@ public partial class PlatformManagementHttpApiHostModule
private void PreConfigureApp()
{
AbpSerilogEnrichersConsts.ApplicationName = "Platform";
AbpSerilogEnrichersConsts.ApplicationName = ApplicationName;
PreConfigure<AbpSerilogEnrichersUniqueIdOptions>(options =>
{
@ -158,7 +158,7 @@ public partial class PlatformManagementHttpApiHostModule
{
Configure<AbpAuditingOptions>(options =>
{
options.ApplicationName = "Platform";
options.ApplicationName = ApplicationName;
// 是否启用实体变更记录
var entitiesChangedConfig = configuration.GetSection("App:TrackingEntitiesChanged");
if (entitiesChangedConfig.Exists() && entitiesChangedConfig.Get<bool>())
@ -324,6 +324,11 @@ public partial class PlatformManagementHttpApiHostModule
};
});
if (isDevelopment)
{
services.AddAlwaysAllowAuthorization();
}
if (!isDevelopment)
{
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);

1
aspnet-core/services/LY.MicroService.PlatformManagement.HttpApi.Host/appsettings.Development.json

@ -8,6 +8,7 @@
"tag": "Platform"
},
"App": {
"TrackingEntitiesChanged": true,
"Forwarded": {
"ForwardedHeaders": 5,
"KnownProxies": [

Loading…
Cancel
Save