feat: 接入 UserHome 前端业务代码(含真实 src/依赖,保留 projman 标准 CI)
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<div class="used-app-wrap">
|
||||
<div class="used-app-list" ref="refApplist">
|
||||
<template v-for="item in appList" :key="item.id + ''">
|
||||
<ContextMenu
|
||||
class="app-container"
|
||||
:title="item.name"
|
||||
@on-change="onMenuChange"
|
||||
@select="onMenuSel"
|
||||
:menu="[
|
||||
{
|
||||
label: '从桌面删除',
|
||||
appId: item.id,
|
||||
id: '1',
|
||||
},
|
||||
{
|
||||
label: '新窗口打开',
|
||||
appId: item.appId,
|
||||
id: '2',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a @click="openLinkPage(item)">
|
||||
<img v-if="item.type == 1" :src="$utils.appIconPath(item.icon)" />
|
||||
<img
|
||||
v-else-if="item.type == 2"
|
||||
:src="$conf.fileHost + '/image/lightapp.default.png'"
|
||||
v-real-img="$conf.serviceHost + item.icon"
|
||||
/>
|
||||
|
||||
<div class="app-name">{{ item.name }}</div>
|
||||
</a>
|
||||
</ContextMenu>
|
||||
</template>
|
||||
</div>
|
||||
<vue-drag-resize
|
||||
v-if="isDragShowComp"
|
||||
:class="{ 'widget-wrap__drag': dragObj.isDrag }"
|
||||
contentClass="widget-wrap"
|
||||
:x="dragPositionObj.xaxis"
|
||||
:y="dragPositionObj.yaxis"
|
||||
:w="dragPositionObj.width"
|
||||
:h="dragPositionObj.height"
|
||||
:parentW="dragObj.parentW"
|
||||
:parentH="dragObj.parentH"
|
||||
:minh="248"
|
||||
:z="10"
|
||||
:isActive="true"
|
||||
:parentLimitation="true"
|
||||
:isDraggable="dragObj.isDrag"
|
||||
:isResizable="true"
|
||||
:preventActiveBehavior="true"
|
||||
:stickSize="5"
|
||||
:sticks="['tm', 'bm']"
|
||||
@clicked="onDragChange"
|
||||
@dragstop="onDragResizestop($event, 'drag')"
|
||||
@resizestop="onDragResizestop($event, 'resize')"
|
||||
>
|
||||
<div class="widget-header">
|
||||
<Dropdown
|
||||
placement="bottom-start"
|
||||
:visible="dragObj.selVisible"
|
||||
@on-click="onWidgetSelItemClick"
|
||||
trigger="custom"
|
||||
>
|
||||
<div class="operate-btn" @click="showWidgetSelClick">
|
||||
<SvgIcon class="fl" name="widget-add" size="24" color="#cbd5df" hoverColor="#fff" />
|
||||
</div>
|
||||
<template #list>
|
||||
<DropdownMenu style="min-width: 120px; max-height: 200px; overflow-y: auto">
|
||||
<DropdownItem :key="item.k" :name="item.k" v-for="item in widgetSel">
|
||||
<div class="fix">
|
||||
<div class="fl">{{ item.v }}</div>
|
||||
<SvgIcon
|
||||
v-if="!!item.checked"
|
||||
class="fr mt-3"
|
||||
name="widget-checked"
|
||||
color="#2d8cf0"
|
||||
size="14"
|
||||
></SvgIcon>
|
||||
</div>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
|
||||
<div class="widget-header-drag" @mousedown="(e:any)=>dragObj.isDrag = e.buttons === 1"></div>
|
||||
<div class="operate-btn" @click="onClickWidgetHide">
|
||||
<SvgIcon class="fr" name="widget-sub" size="24" color="#cbd5df" hoverColor="#fff" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget-body" v-show="!dragObj.isDrag">
|
||||
<vuescroll :ops="widgetOps" v-if="widgetAppList.length > 0">
|
||||
<div class="widget-item pr" v-for="item in widgetAppList">
|
||||
<div class="widget-app-header">
|
||||
<span>{{ item.name }}</span>
|
||||
<SvgIcon
|
||||
@click="onWidgetSelItemClick(item.id)"
|
||||
class="fr"
|
||||
name="widget-close"
|
||||
size="18"
|
||||
color="#999999"
|
||||
hoverColor="#646566"
|
||||
/>
|
||||
</div>
|
||||
<Skeleton
|
||||
style="padding: 15px"
|
||||
:loading="item.loading"
|
||||
:title="false"
|
||||
:paragraph="{ rows: 4 }"
|
||||
animated
|
||||
/>
|
||||
<iframe
|
||||
v-show="!item.loading"
|
||||
:style="{ height: item.height + 'px' }"
|
||||
:id="'iframe_widget_' + item.id"
|
||||
:data-id="item.id"
|
||||
@load="onIframeLoad(item.id)"
|
||||
:src="item.url"
|
||||
class="widget-app-iframe"
|
||||
></iframe>
|
||||
</div>
|
||||
</vuescroll>
|
||||
<template v-if="widgetAppList.length == 0">
|
||||
<div class="widget-empty-box">
|
||||
<SvgIcon name="widget-empty" color="#2d8cf0" size="120"></SvgIcon>
|
||||
<h4>单击 + 按钮可添加小工具。</h4>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</vue-drag-resize>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ContextMenu from "@/components/ContextMenu.vue";
|
||||
import { ref, reactive, onMounted, onUnmounted, computed, watch, watchEffect, nextTick } from "vue";
|
||||
import BaseApi from "@/api/BaseApi";
|
||||
// @ts-ignore
|
||||
import VueDragResize from "vue-drag-resize/src";
|
||||
import { useDefaultStore } from "@/store";
|
||||
import SvgIcon from "@/components/SvgIcon.vue";
|
||||
import { cloneDeep, debounce, difference, includes, once, pick, remove } from "lodash";
|
||||
import UserSettingApi from "@/api/UserSettingApi";
|
||||
import { bschLinkPage } from "@/utils/bschLinkPage";
|
||||
import {
|
||||
BschLoadTypeEnum,
|
||||
BschTransferRoleInfo,
|
||||
IframePageParmas,
|
||||
LinkOpenTypeEnum,
|
||||
StateEnum,
|
||||
WidgetPosition,
|
||||
WidgetPositionResp,
|
||||
} from "@/types";
|
||||
import { bschRoleEncode } from "@/utils/bschRoleEncode";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import mittBus from "@/utils/mitt";
|
||||
import vuescroll from "vuescroll";
|
||||
import useCurrentInstance from "@/utils/hook/useCurrentInstance";
|
||||
import { Message } from "view-ui-plus";
|
||||
import { useDraggable } from "vue-draggable-plus";
|
||||
|
||||
const { $conf, $utils } = useCurrentInstance();
|
||||
const store = useDefaultStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const refApplist = ref();
|
||||
const isDragShow = ref(false),
|
||||
isDesltopAppDefault = ref(false),
|
||||
appList = ref<any[]>([]);
|
||||
const dragObj = reactive({
|
||||
isDrag: false,
|
||||
selVisible: false,
|
||||
parentW: 0,
|
||||
parentH: 0,
|
||||
});
|
||||
|
||||
const widgetOps = {
|
||||
vuescroll: {},
|
||||
scrollPanel: {},
|
||||
rail: {},
|
||||
bar: {
|
||||
background: "#333",
|
||||
},
|
||||
};
|
||||
const widgetAppList = ref<any[]>([]);
|
||||
const dragPositionObj = reactive<Partial<WidgetPosition>>({});
|
||||
|
||||
const isDragShowComp = computed(() => {
|
||||
const tempObj = cloneDeep(store.getWidgetDragObj);
|
||||
return tempObj.isWidgetShow && isDragShow.value;
|
||||
});
|
||||
|
||||
const compMenuId = computed(() => {
|
||||
let menuList = store.curUserMenuList.find(x => x.url == route.path);
|
||||
return menuList ? menuList.id : -1;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
const tempObj = cloneDeep(store.getWidgetDragObj);
|
||||
let isOutRangeX = false,
|
||||
isOutRangeY = false; //是否超出范围
|
||||
if (tempObj.xaxis == 0 && tempObj.yaxis === 0) {
|
||||
tempObj.xaxis = dragObj.parentW - 540;
|
||||
tempObj.yaxis = 88;
|
||||
isOutRangeX = dragObj.parentW < 900;
|
||||
} else {
|
||||
isOutRangeX = dragObj.parentW < tempObj.width + tempObj.xaxis;
|
||||
isOutRangeY = dragObj.parentH < tempObj.height + tempObj.yaxis;
|
||||
}
|
||||
if (isOutRangeX) tempObj.xaxis = dragObj.parentW - tempObj.width - 10;
|
||||
if (isOutRangeY) tempObj.yaxis = dragObj.parentH - tempObj.height - 10;
|
||||
Object.assign(dragPositionObj, tempObj);
|
||||
reloadDragWrap();
|
||||
});
|
||||
|
||||
const draggable = useDraggable(refApplist, appList, {
|
||||
animation: 150,
|
||||
onStart() {
|
||||
// console.log("start");
|
||||
},
|
||||
async onUpdate() {
|
||||
let ids = appList.value.map(x => x.id);
|
||||
let appIds = appList.value.map(x => x.appId);
|
||||
if (isDesltopAppDefault.value) {
|
||||
await UserSettingApi.sortUserDesktopApp(appIds);
|
||||
isDesltopAppDefault.value = false;
|
||||
} else await UserSettingApi.sortUserDesktopApp(ids);
|
||||
// console.log("update");
|
||||
},
|
||||
});
|
||||
onMounted(async () => {
|
||||
await getAppList();
|
||||
dragObj.parentW = window.innerWidth;
|
||||
dragObj.parentH = window.innerHeight;
|
||||
window.addEventListener("resize", cancalDebounce);
|
||||
window.addEventListener("message", iframeCallback);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", cancalDebounce);
|
||||
window.removeEventListener("message", iframeCallback);
|
||||
});
|
||||
|
||||
const widgetSel = computed(() => store.getWidgetSel);
|
||||
|
||||
watch(() => store.curRoleUid, getAppList);
|
||||
|
||||
watch(
|
||||
() => store.getWidgetAppList,
|
||||
val => {
|
||||
widgetAppList.value = val.map(x => {
|
||||
return { ...x, loading: true, height: 20 };
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
function onMenuChange(curDom: any, state: boolean) {
|
||||
$(curDom).toggleClass("app-container__focus", state);
|
||||
}
|
||||
async function onMenuSel(item: any) {
|
||||
if (item.id == 1) {
|
||||
try {
|
||||
const find = appList.value.find(x => x.id == item.appId);
|
||||
await UserSettingApi.deleteUserDesktopAppById(find.id, find.appId);
|
||||
Message.success("操作成功");
|
||||
await getAppList();
|
||||
} catch (error) {
|
||||
Message.error("内部服务错误");
|
||||
}
|
||||
}
|
||||
if (item.id == 2) {
|
||||
let find = appList.value.find(x => x.appId == item.appId);
|
||||
bschLinkPage({ url: find.url, type: LinkOpenTypeEnum.Open });
|
||||
}
|
||||
}
|
||||
|
||||
function onDragChange(e: any) {
|
||||
var t: Element = e.target;
|
||||
if (!!t && !includes(t.classList, "widget-header-drag")) dragObj.isDrag = false;
|
||||
}
|
||||
|
||||
function onDragResizestop(opt: any, type: "drag" | "resize") {
|
||||
const { left, top, width, height } = opt;
|
||||
if (type === "drag") {
|
||||
dragObj.isDrag = false;
|
||||
if (left === dragPositionObj.xaxis && top === dragPositionObj.yaxis) return false;
|
||||
} else {
|
||||
}
|
||||
let positionInfo: WidgetPositionResp = {
|
||||
xaxis: left,
|
||||
yaxis: top,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
store.updateWidgetInfo(positionInfo);
|
||||
debounceSaveUserWidget(positionInfo);
|
||||
}
|
||||
const debounceSaveUserWidget = debounce(saveUserWidget, 1000);
|
||||
async function saveUserWidget(info: WidgetPositionResp) {
|
||||
info.id = store.curUserSettingInfo?.id;
|
||||
info.schId = store.getCurSchId;
|
||||
await UserSettingApi.saveUserWidget(info);
|
||||
}
|
||||
|
||||
const cancalDebounce = debounce(setDragWindowInfo, 150);
|
||||
function setDragWindowInfo() {
|
||||
dragObj.parentW = window.innerWidth;
|
||||
dragObj.parentH = window.innerHeight;
|
||||
}
|
||||
|
||||
function reloadDragWrap() {
|
||||
if (!store.getWidgetDragObj.isWidgetShow) return;
|
||||
isDragShow.value = false;
|
||||
requestAnimationFrame(() => {
|
||||
isDragShow.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function onWidgetSelItemClick(appId: any) {
|
||||
const curAppIdStrs = store.curUserSettingInfo.widgetAppIds;
|
||||
const curAppIds = store.getWidgetAppIds;
|
||||
let find = store.getWidgetSel.find(x => x.k == appId);
|
||||
if (!find) return false;
|
||||
let checkAppIds: number[] = [];
|
||||
if (!!find.checked) {
|
||||
if (curAppIdStrs == null) checkAppIds = difference(curAppIds, [find.k]);
|
||||
else {
|
||||
let tempArr = cloneDeep(curAppIds);
|
||||
checkAppIds = remove(tempArr, (x: number) => x !== appId);
|
||||
}
|
||||
} else {
|
||||
checkAppIds = [...curAppIds, find.k];
|
||||
}
|
||||
console.log(checkAppIds);
|
||||
store.curUserSettingInfo.widgetAppIds = checkAppIds.join(",");
|
||||
const id = store.curUserSettingInfo?.id;
|
||||
await UserSettingApi.saveUserAppIds(id, checkAppIds.join(","));
|
||||
console.log("后续操作");
|
||||
}
|
||||
function hideWidgetSel() {
|
||||
dragObj.selVisible = false;
|
||||
}
|
||||
|
||||
function showWidgetSelClick() {
|
||||
dragObj.selVisible = true;
|
||||
window.removeEventListener("click", hideWidgetSel);
|
||||
requestAnimationFrame(() => {
|
||||
window.addEventListener("click", hideWidgetSel, { once: true });
|
||||
});
|
||||
}
|
||||
async function onClickWidgetHide() {
|
||||
const id = store.curUserSettingInfo?.id;
|
||||
if (!!store.curUserSettingInfo) store.curUserSettingInfo.isWidgetShow = false;
|
||||
await UserSettingApi.toggleWidgetShow(id);
|
||||
}
|
||||
|
||||
function onIframeLoad(id: number) {
|
||||
const ele: any = document.getElementById("iframe_widget_" + id);
|
||||
if (!ele) return false;
|
||||
const msgObj = {
|
||||
roleInfo: store.curRoleInfo,
|
||||
id: id,
|
||||
};
|
||||
const msgStr = bschRoleEncode(msgObj);
|
||||
ele.contentWindow.postMessage(msgStr, "*");
|
||||
}
|
||||
|
||||
function iframeCallback(ev: MessageEvent<any>) {
|
||||
const { id, height } = JSON.parse(ev.data);
|
||||
let find = widgetAppList.value.find(x => x.id == id);
|
||||
if (!find) return;
|
||||
find.height = height;
|
||||
requestAnimationFrame(() => {
|
||||
find.loading = false;
|
||||
});
|
||||
//console.log(find, id, height);
|
||||
}
|
||||
async function getAppList() {
|
||||
appList.value = await UserSettingApi.getUserDesktopAppList();
|
||||
let arr = cloneDeep(appList.value);
|
||||
isDesltopAppDefault.value = arr.filter(x => x.id == null).length === arr.length;
|
||||
}
|
||||
|
||||
async function openLinkPage(item: any) {
|
||||
let options: Parameters<typeof bschLinkPage>[0];
|
||||
options = {
|
||||
url: item.url as string,
|
||||
type: item.linkType,
|
||||
title: item.name,
|
||||
};
|
||||
|
||||
if (item.type == 1 && $conf.ifameAppIdArr.includes(item.appId)) {
|
||||
options.type = LinkOpenTypeEnum.IFrame;
|
||||
options.isHeader = true;
|
||||
}
|
||||
|
||||
if (item.linkType == LinkOpenTypeEnum.Modal) {
|
||||
if (!!item.layout) {
|
||||
options.modalWidth = JSON.parse(item.layout).width;
|
||||
options.modalHeight = JSON.parse(item.layout).height;
|
||||
}
|
||||
}
|
||||
bschLinkPage(options);
|
||||
//bschLinkPage
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import url("@/assets/styles/componets/_widget.scss");
|
||||
.used-app-wrap {
|
||||
height: calc(100% - 88px);
|
||||
}
|
||||
.used-app-list {
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
justify-content: flex-start;
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
align-content: flex-start;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.widget-wrap .vdr-stick-tm,
|
||||
.vdr-stick-bm {
|
||||
width: 380px !important;
|
||||
left: 0 !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.vdr.active::before {
|
||||
content: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user