| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 |
- import { isNum, toStyleStr } from "./tools.js";
- import { eventBus, EVEN_VAR } from "./EventBus.js";
- // import { debounce } from "../lib/tools.js";
- // 首页组件的名字
- const winName = {
- launch: "launch",
- Notice: "Notice",
- Statistics: "Statistics",
- TodoList: "TodoList",
- UrgingList: "UrgingList",
- UserInfo: "UserInfo",
- };
- // 尺寸窗口名字
- const size2Win = (name) => `${name}-${document.body.clientWidth}`;
- // 加载尺寸
- const loadWinSize = function (name) {
- const val = localStorage.getItem(size2Win(name));
- if (val) {
- try {
- return JSON.parse(val) || {};
- } catch (err) {
- return {};
- }
- } else {
- return {};
- }
- };
- const saveWinSize = function (name, obj) {
- localStorage.setItem(size2Win(name), JSON.stringify(obj));
- };
- // 全局头部
- export const GlobalHeader = {
- name: 'GlobalHeader',
- props: {
- menuData: {
- type: Array,
- required: true
- },
- iconItems: {
- type: Array,
- required: true
- }
- },
- setup(props) {
- //原值为'/newUI/skin/easy/images/logo/full-logo.png' Ben(20251205)
- const fullLogo = Vue.ref('/skin/easy/images/logo/full-logo.png'); // 需要指定完整路径
- const convertMenuData = (menuData) => {
- return menuData.map(item => ({
- id: item.id, //菜单id
- pid: item.pid, //父菜单id
- label: item.desc, //菜单名称
- component: item.url, //菜单链接
- js: item.js, //菜单要执行的js
- class: item.icon, // 如果icon为空则使用默认class
- itemType: item.type, //菜单 1:菜单项 2:菜单组
- }));
- };
- const menuItemsNew = Vue.ref(convertMenuData(props.menuData));
- Vue.watchEffect(() => {
- if (props.menuData && props.menuData.length) {
- menuItemsNew.value = convertMenuData(props.menuData);
- console.log('Header menu items updated:', menuItemsNew.value);
- }
- });
-
-
- const menuItems = Vue.ref([]);
- const breadCrumbs = Vue.ref([
- { label: '首页', component: '/index.html' },
- ]);
- const iconItems = Vue.ref(props.iconItems);
- const onClickMenuItemsNew = (item) => {
- console.log(item.component, item.js)
- if (item.component && item.js) {
- eval(item.js)
- } else if(item.component){
- eventBus.publish(EVEN_VAR.currentPage, item.component);
- } else {
- eval(item.js)
- }
- };
- const onClickMenuItem = (item) => {
- if (item.type == 'page') {
- eventBus.publish(EVEN_VAR.currentPage, item.component);
- breadCrumbs.value[0] = item
- } else if (item.type == 'dialog') {
- SS.openDialog({
- headerTitle: item.label,
- src: '.'+item.component,
- height: item.height,
- width:item.width
- });
- }
- };
- const breadCrumbClick = (item) => {
- eventBus.publish(EVEN_VAR.currentPage, item.component);
- breadCrumbs.value[0] = item
- }
- const gotoSearch = () => {
- eventBus.publish(EVEN_VAR.showGlobalSearchDialog);
- };
- const SsIcon = Vue.resolveComponent('ss-icon');
- const SsHeaderIcon = Vue.resolveComponent('ss-header-icon');
- const SsGolbalMenuIcon = Vue.resolveComponent('ss-golbal-menu-icon');
- // 引入搜索框
- const SsSearch = Vue.resolveComponent('ss-search');
- // 处理面包屑导航
- const renderBreadcrumbs = () => {
- const breadcrumbElements = [Vue.h(SsIcon, { class: "home-icon", name: "home", type: "common", size: "22px" })];
- // 遍历面包屑数组,添加面包屑和分隔符
- breadCrumbs.value.forEach((crumb, index) => {
- // 添加面包屑链接
- breadcrumbElements.push(
- Vue.h('span', { onClick: () => breadCrumbClick(crumb) }, crumb.label)
- );
- // 除了最后一个元素,每个面包屑后添加分隔符图标
- if (index < breadCrumbs.value.length - 1) {
- breadcrumbElements.push(
- Vue.h(SsIcon, { class: "split-icon", size: "12px", name: "double-arrow-right", type: "common" })
- );
- }
- });
- return breadcrumbElements;
- };
- return () => Vue.h('div', { class: 'block-self flex-between-center global-header-container' }, [
- Vue.h('div', { class: 'icon-area flex-start-center' }, [
- Vue.h('div', { class: 'logo', onClick: () => console.log('Home clicked') }, [
- Vue.h('div', { class: 'img' }, [
- Vue.h('img', { src: fullLogo.value })
- ]),
- Vue.h('div', { class: 'menu', onClick: Vue.withModifiers(() => { }, ['stop']) },
- // menuItems.value.map(item =>
- // Vue.h('div', { onClick: () => onClickMenuItem(item) },[
- // Vue.h(SsGolbalMenuIcon, { class: item.class || '' }),
- // item.label
- // ])
- // )
- menuItemsNew.value.map(item =>
- Vue.h('div', { onClick: () => onClickMenuItemsNew(item) },[
- Vue.h(SsGolbalMenuIcon, { class: item.class || '' }),
- item.label
- ])
- )
- )
- ]),
- // Vue.h('div', { class: 'bread-crumb', style: { height: "100% !important" } }, [
- // Vue.h('div', { class: 'content' }, renderBreadcrumbs())
- // ])
- ]),
- Vue.h('div', { class: 'menu-area' },
- Vue.h('div', { class: 'search-area' }, [
- Vue.h(SsSearch, { theme: 'dark', placeholder: '跨对象搜索', onClick: gotoSearch })
- ]),
- iconItems.value.map(icon =>
- Vue.h('div', { class: 'icon-item', onClick: icon.action, style: { display: icon.condition ? (icon.condition() ? 'block' : 'none') : 'block' } }, [
- Vue.h(SsHeaderIcon, { class: icon.class })
- ])
- )
- )
- ]);
- }
- };
- // 全局菜单
- export const GlobalMenu = {
- name: 'GlobalMenu',
- props: {
- menuItems: {
- type: Array,
- required: true
- },
- initialSize: {
- type: String,
- default: 'min'
- },
- // onMenuClick: {
- // type: Function,
- // required: true,
- // default: () => {}
- // }
- },
- setup(props) {
- const menuItemsNew = Vue.ref([]);
-
- const activeItem = Vue.ref(''); // 默认选中第一个菜单项
-
- // 将后台返回的菜单数据转换为树结构
- const convertMenuDataToTree = (menuData) => {
- // 先转换格式
- const formattedData = menuData.map(item => ({
- id: item.id,
- pid: item.pid,
- name: item.desc,
- component: item.url,
- js: item.js,
- // v3.0 优先使用 item.icon,没有才用 menu-base-icon + 默认图标 by xu 20251215
- class: item.icon || (item.type == 1 ? 'menu-base-icon icon-folder-close' : 'menu-base-icon icon-shouye'),
- itemType: item.type,
- children: []
- }));
-
- // 创建一个映射表,方便查找
- const map = {};
- formattedData.forEach(item => {
- map[item.id] = item;
- });
-
- // 构建树结构
- const treeData = [];
- formattedData.forEach(item => {
- if (item.pid && map[item.pid]) {
- // 如果有父节点,就放到父节点的children中
- if (!map[item.pid].children) {
- map[item.pid].children = [];
- }
- map[item.pid].children.push(item);
- } else {
- // 没有父节点就是顶层节点
- treeData.push(item);
- }
- });
-
- return treeData;
- };
- // 添加 watchEffect 来监听 props.menuItems 的变化
- Vue.watchEffect(() => {
- if (props.menuItems && props.menuItems.length) {
- menuItemsNew.value = convertMenuDataToTree(props.menuItems);
- // 初始化 activeItem
- const firstItem = menuItemsNew.value[0];
- if(firstItem?.component){
- // 首页有URL,直接设置为首页
- activeItem.value = firstItem.name;
- eventBus.publish(EVEN_VAR.currentPage, firstItem.component);
- } else if(firstItem?.children && firstItem.children.length > 0){
- // 首页没有URL,但有子菜单,设置为第一个子菜单
- activeItem.value = firstItem.children[0].name;
- eventBus.publish(EVEN_VAR.currentPage, firstItem.children[0].component);
- } else {
- activeItem.value = '';
- }
- console.log('Menu items updated:', menuItemsNew.value);
- }
- });
- // v3.0 使用 ss-icon 组件替代 SsNavIcon by xu 20251215
- const SsIcon = Vue.resolveComponent('ss-icon');
- // 移动状态控制逻辑到组件内
- const leftSideTypeDict = {
- min: { key: "min", icon: "arrow-double-right" },
- max: { key: "max", icon: "arrow-double-left" },
- };
-
- const leftSideType = Vue.ref(leftSideTypeDict[props.initialSize]);
- let waitLeftSideChangeTimer = null;
-
- // 控制子菜单展开状态
- const expandedMenus = Vue.ref(new Set());
- // 点击子菜单不高亮父菜单 by xu 20251211
- const shouldHighlightMenu = (item) => {
- if (!item) {
- return false;
- }
- const hasChildren = Array.isArray(item.children) && item.children.length > 0;
- return !hasChildren && activeItem.value === item.name;
- };
- // ===== 三种菜单模式管理 =====
- const menuModeDict = {
- collapse: { key: "collapse", label: "收起", width: "60px" },
- fixed: { key: "fixed", label: "固定", width: "60px" },
- expand: { key: "expand", label: "展开", width: "230px" }
- };
- // 当前菜单模式(默认收起)
- const currentMenuMode = Vue.ref(menuModeDict.collapse);
- // 切换菜单模式(循环:收起 → 固定 → 展开 → 收起)
- const toggleMenuMode = () => {
- const modeOrder = ['collapse', 'fixed', 'expand'];
- const currentIndex = modeOrder.indexOf(currentMenuMode.value.key);
- const nextIndex = (currentIndex + 1) % modeOrder.length;
- const nextMode = menuModeDict[modeOrder[nextIndex]];
- currentMenuMode.value = nextMode;
- // 更新 CSS 变量
- updateLayoutWidth(nextMode.width);
- };
- // 更新布局宽度
- const updateLayoutWidth = (width) => {
- const layoutContainer = document.querySelector('.layout-container');
- if (layoutContainer) {
- layoutContainer.style.setProperty('--left-side-width', width);
- }
- };
- const onMenuClick = (item) => {
- console.log(item)
- console.log(activeItem.value)
- if(item.component && item.js){
- eval(item.js)
- } else if(item.component){
- eventBus.publish(EVEN_VAR.currentPage, item.component);
- }else{
- eval(item.js)
- }
- }
- const toggleLeftSideType = () => {
- leftSideType.value =
- leftSideType.value.key === 'min' ? leftSideTypeDict.max : leftSideTypeDict.min;
- };
- const doChangeLeftSideType2Max = () => {
- // 只有在收起模式下才响应鼠标悬停
- if (currentMenuMode.value.key === 'collapse' && leftSideType.value.key === 'min') {
- // 即刻展开,不需要延迟
- clearTimeout(waitLeftSideChangeTimer);
- leftSideType.value = leftSideTypeDict.max;
- }
- };
- const onLeaveLeftMenuArea = () => {
- // 只有在收起模式下才响应鼠标离开
- if (currentMenuMode.value.key === 'collapse') {
- if (waitLeftSideChangeTimer) {
- clearTimeout(waitLeftSideChangeTimer);
- waitLeftSideChangeTimer = null;
- }
- leftSideType.value = leftSideTypeDict.min;
- }
- };
- return () => Vue.h('div', { class: 'left-side' }, [
- Vue.h('div', {
- class: 'left-side-container',
- 'data-mode': currentMenuMode.value.key, // 添加模式标识
- size: leftSideType.value.key
- }, [
- // ===== 固定区域:首页 =====
- Vue.h('div', { class: 'fixed-top' }, [
- menuItemsNew.value.length > 0 ? Vue.h('div', {
- class: ['menu-item', 'level-1', { active: shouldHighlightMenu(menuItemsNew.value[0]) }],
- onClick: () => {
- const firstItem = menuItemsNew.value[0];
- activeItem.value = firstItem.name;
- onMenuClick(firstItem);
- }
- }, [
- Vue.h('div', { class: 'menu-item-content' }, [
- // v3.0 使用 ss-icon + 双 class (图标类 + menu-icon 组类) by xu 20251215
- Vue.h(SsIcon, { class: 'menu-base-icon icon-shouye'}),
- Vue.h('div', { class: 'menu-item-label' }, menuItemsNew.value[0]?.name || '首页')
- ])
- ]) : null
- ]),
- // ===== 可滚动区域:其他菜单项 =====
- Vue.h('div', {
- class: 'scrollable-content',
- onMouseenter: doChangeLeftSideType2Max,
- onMousemove: doChangeLeftSideType2Max,
- onMouseleave: onLeaveLeftMenuArea
- }, [
- // 菜单项列表(跳过首页)
- ...menuItemsNew.value.slice(1).map(icon => [
- // 父菜单项
- Vue.h('div', {
- class: ['menu-item', 'level-1', { active: shouldHighlightMenu(icon) }],
- onClick: () => {
- if (icon.children && icon.children.length > 0) {
- // 有子菜单:仅切换展开/收起,不设置 activeItem(不高亮)
- if (expandedMenus.value.has(icon.name)) {
- expandedMenus.value.delete(icon.name);
- } else {
- expandedMenus.value.add(icon.name);
- }
- } else {
- // 无子菜单:设置 activeItem 并导航
- activeItem.value = icon.name;
- onMenuClick(icon);
- }
- }
- }, [
- Vue.h('div', { class: 'menu-item-content' }, [
- // v3.0 收起时显示自定义图标,展开时统一显示 folder-open by xu 20251215
- Vue.h(SsIcon, {
- class: (expandedMenus.value.has(icon.name)
- ? 'icon-folder-open'
- : (icon.class || 'menu-base-icon icon-folder-close')) + ' menu-icon'
- }),
- Vue.h('div', { class: 'menu-item-label' }, icon.name || ''),
- // 有子菜单时显示小圆点(在一级菜单右上角)
- icon.children && icon.children.length > 0 ?
- Vue.h('div', { class: 'has-children-dot' }) : null
- ])
- ]),
- // 子菜单项(与父菜单项同级)
- ...(icon.children && expandedMenus.value.has(icon.name) ?
- icon.children.map(child =>
- Vue.h('div', {
- class: ['menu-item', 'level-2', { active: activeItem.value === child.name }],
- onClick: (e) => {
- activeItem.value = child.name;
- e.stopPropagation();
- onMenuClick(child);
- }
- }, [
- Vue.h('div', { class: 'menu-item-content' }, [
- // v3.0 使用 ss-icon + 双 class by xu 20251215
- Vue.h(SsIcon, {
- class: (child.class || '') + ' menu-icon'
- }),
- Vue.h('div', { class: 'menu-item-label' }, child.name)
- ])
- ])
- ) : []
- )
- ]).flat()
- ]),
- // ===== 固定区域:菜单模式按钮 =====
- Vue.h('div', { class: 'fixed-bottom' }, [
- Vue.h('div', {
- class: 'menu-mode-btn',
- onClick: toggleMenuMode
- }, [
- Vue.h(SsIcon, { class: 'menu-base-icon icon-qiehuan' })
- ])
- ])
- ])
- ]);
- }
- };
- // 基础组件 首页组件头部
- export const HeaderContainer = {
- name: 'HeaderContainer',
- props: {
- title: String,
- icon: String,
- },
- emits: ['setting', 'refresh'],
- setup(props, { emit }) {
- const onSetting = () => {
- emit('setting');
- };
- const onRefresh = () => {
- emit('refresh');
- };
- return {
- props,
- onSetting,
- onRefresh
- };
- },
- render() {
- const SsIcon = Vue.resolveComponent('ss-icon');
-
- return Vue.h('div', { class: 'edit-box-header-container' }, [
- Vue.h('div', { class: ['title', { visibility: !!(this.props.icon || this.props.title) }] }, [
- this.props.icon ? Vue.h('div', { class: 'icon', onClick: this.onRefresh }, [
- Vue.h(SsIcon, { class: 'normal', name: this.props.icon, size: '22px' }),
- Vue.h(SsIcon, { class: 'hover', name: 'refresh', size: '22px' })
- ]) : null,
- Vue.h('div', this.props.title)
- ]),
- Vue.h('div', { class: 'handle-bar' }, [
- Vue.h('div', { class: 'left-bar' }), // Assuming left-bar is empty
- Vue.h('div', { class: 'setting', onClick: this.onSetting }, [
- Vue.h(SsIcon, { name: 'setting', size: '22px' })
- ])
- ])
- ]);
- }
- };
- // 基础组件 首页组件边框
- export const EditBox = {
- name: 'EditBox',
- setup(props, { emit, slots }) {
- const mousePos = Vue.reactive({
- rightCenter: "right-center",
- rightBottom: "right-bottom",
- bottomCenter: "bottom-center",
- });
- const curActionMousePos = Vue.ref("");
- const editBoxContainer = Vue.ref(null);
- const onMousemove = (e) => {
- if (e.buttons === 1 && curActionMousePos.value) {
- const dom = editBoxContainer.value;
- if (dom) {
- const zoom = Number(document.body.style.zoom || 1);
- const winRect = dom.getBoundingClientRect();
- const { x: xSource, y: ySource } = e;
- const x = xSource / zoom;
- const y = ySource / zoom;
- const val = {
- left: winRect.left,
- top: winRect.top,
- width: winRect.width,
- height: winRect.height,
- };
- const toHObj = (source) => ({
- ...source,
- height: Math.abs(y - source.top),
- top: y >= 0 ? source.top : source.top + (y - source.top),
- });
- const toWObj = (source) => ({
- ...source,
- width: Math.abs(x - source.left),
- left: x >= 0 ? source.left : source.left + (x - source.left),
- });
- if (curActionMousePos.value === mousePos.bottomCenter) {
- emit("size", toHObj(val));
- } else if (curActionMousePos.value === mousePos.rightCenter) {
- emit("size", toWObj(val));
- } else if (curActionMousePos.value === mousePos.rightBottom) {
- emit("size", toWObj(toHObj(val)));
- }
- }
- }
- };
- const onMouseup = () => {
- curActionMousePos.value = "";
- };
- const onMouseDown = (pos) => {
- console.log(pos)
- curActionMousePos.value = pos;
- };
- Vue.onMounted(() => {
- document.body.addEventListener("mousemove", onMousemove);
- document.body.addEventListener("mouseup", onMouseup);
- });
- Vue.onUnmounted(() => {
- document.body.removeEventListener("mousemove", onMousemove);
- document.body.removeEventListener("mouseup", onMouseup);
- });
- const SsIcon = Vue.resolveComponent('ss-icon');
- return () => Vue.h('div', { class: 'edit-box-container', ref: editBoxContainer }, [
- Vue.h('div', { class: ['edit-tools', { active: !!curActionMousePos.value }] }, [
- Vue.h('div', { class: 'close' }, [
- Vue.h(SsIcon, { name: "close-mark-fill", size: "36px" })
- ]),
- Vue.h('div', { class: 'right-center', onMousedown: () => onMouseDown(mousePos.rightCenter) }, [
- Vue.h('div', { class: 'icon' }, [
- Vue.h(SsIcon, { name: "resize", size: "30px" })
- ])
- ]),
- Vue.h('div', { class: 'right-bottom', onMousedown: () => onMouseDown(mousePos.rightBottom) }, [
- Vue.h('div', { class: 'icon' }, [
- Vue.h(SsIcon, { name: "resize", size: "30px" })
- ])
- ]),
- Vue.h('div', { class: 'bottom-center', onMousedown: () => onMouseDown(mousePos.bottomCenter) }, [
- Vue.h('div', { class: 'icon' }, [
- Vue.h(SsIcon, { name: "resize", size: "30px" })
- ])
- ])
- ]),
- Vue.h('div', { class: 'content-area' }, slots.default ? slots.default() : [])
- ]);
- }
- };
- // 基础组件 头像
- export const Avatar = {
- name: 'Avatar',
- props: {
- url: {
- type: String,
- required: true
- },
- size: {
- type: [String, Number],
- default: 50
- },
- unit: {
- type: String,
- default: 'px'
- },
- shape: {
- validator: function (value) {
- return ['circle', 'round'].includes(value);
- },
- default: 'circle'
- }
- },
- setup(props) {
- // 计算属性,转换尺寸并添加单位
- const style = Vue.computed(() => {
- const addUnit = (n) => isNum(n) ? `${n}${props.unit}` : (n || '');
- const styleObj = {
- width: addUnit(props.size),
- height: addUnit(props.size),
- };
- return toStyleStr(styleObj);
- });
- // 返回渲染函数所需要的数据
- return {
- props,
- style
- };
- },
- render() {
- return Vue.h('img', {
- src: this.props.url,
- style: this.style.value,
- class: ['avatar-container', this.props.shape]
- });
- }
- };
- // 基础组件 文件夹或者文件的组件
- export const FolderContainer = {
- name: 'FolderContainer',
- props: {
- list: {
- type: Array,
- required: true,
- },
- draggable: {
- type: Boolean,
- default: false,
- },
- },
- setup(props, { emit }) {
- const toggleFolder = (index, isFolder) => {
- const item = props.list[index];
- if (isFolder) {
- item.open = !item.open;
- }
- };
- const onClickItem = (item) => {
- emit('click', item);
- };
- const onItemStartDrag = (e, item) => {
- e.dataTransfer.setData("text/plain", JSON.stringify(item));
- console.log("开始拖拽的对象是=>", { item });
- };
- const onItemDrop = (e, item, pre) => {
- var data = e.dataTransfer.getData("text/plain");
- try {
- const obj = JSON.parse(data);
- item.children.push(obj);
- console.log("目标对象=>", { e, item, pre, obj });
- } catch (err) {
- console.log("拖拽的节点不正确", { item, err });
- }
- };
- return {
- toggleFolder,
- onClickItem,
- onItemStartDrag,
- onItemDrop,
- };
- },
- render() {
- const SsIcon = Vue.resolveComponent('ss-icon');
- return Vue.h('div', { class: 'folder-container' }, this.list.map((groupItem, i) => {
- return Vue.h('div', { class: 'group-item', key: i }, [
- Vue.h('div', {
- class: ['group-title', { active: !!groupItem.curActionMousePos }],
- onDrop: $event => this.onItemDrop($event, groupItem),
- onDragover: $event => $event.preventDefault(),
- }, [
- groupItem.type === 'folder' ? Vue.h('div', {
- class: 'folder',
- onClick: $event => this.toggleFolder(i, groupItem.type === 'folder'),
- 'data-num': groupItem.children.length,
- }, [
- groupItem.open ? Vue.h(SsIcon, { name: 'folder-expand-fill' }) : Vue.h(SsIcon, { name: 'folder-collapse-fill' })
- ]) : groupItem.type === 'file' ? Vue.h(SsIcon, { name: 'file' }) : null,
- Vue.h('div', groupItem.title)
- ]),
- groupItem.children.length > 0 && groupItem.open ? Vue.h('ul', {
- class: 'group-childs',
- onDrop: $event => this.onItemDrop($event, groupItem),
- onDragover: $event => $event.preventDefault(),
- }, groupItem.children.map((item, j) => {
- return Vue.h('li', {
- key: j,
- draggable: this.draggable,
- onDragstart: $event => this.onItemStartDrag($event, item),
- onDrop: $event => this.onItemDrop($event, groupItem, item),
- onDragover: $event => $event.preventDefault(),
- onClick: () => this.onClickItem(item),
- }, [
- Vue.h('div', item.title),
- Vue.h('div', item.time)
- ]);
- })) : null
- ]);
- }));
- }
- };
- // 个人卡片
- export const UserInfo = {
- name: 'UserInfo',
- props: {
- obj: {
- type: Object,
- default: () => ({})
- }
- },
- setup(props, { emit }) {
- const winInfo = Vue.reactive(loadWinSize(winName.UserInfo));
- const onSetting = () => {
- console.log("点击了设置按钮");
- };
- const onRefresh = () => {
- console.log("点击了刷新按钮");
- };
- const onSizeChange = (rect) => {
- const style = {};
- for (const key in rect) {
- style[key] = rect[key] + 'px';
- }
- saveWinSize(winName.UserInfo, style);
- Object.assign(winInfo, style);
- };
- return {
- winInfo,
- onSetting,
- onRefresh,
- onSizeChange,
- props
- };
- },
- render() {
- const SsIcon = Vue.resolveComponent('ss-icon');
- return Vue.h('div', { class: 'user-info-container can-resize-box', style: this.winInfo },
- Vue.h(EditBox, {
- onSize: this.onSizeChange
- }, {
- default: () => [
- Vue.h('div', { class: 'header' },
- Vue.h(HeaderContainer, {
- onSetting: this.onSetting,
- onRefresh: this.onRefresh
- })
- ),
- Vue.h('div', { class: 'body' }, [
- Vue.h('div', { class: 'user-info', style: "margin-bottom: 18px" }, [
- Vue.h('div', { class: 'avatar' },
- Vue.h(Avatar, {
- url: '../images/example/user-avatar.png',
- size: '90px'
- })
- ),
- Vue.h('div', { class: 'info' }, [
- Vue.h('p', this.props.obj.name + ',上午好!'),
- Vue.h('p', '最近登录时间:2024年/08/08 13:03'),
- Vue.h('p', '明天有暴雨,记得出门带伞噢!')
- ])
- ]),
- Vue.h('div', { class: 'progress-bar' }, [
- Vue.h('div', { class: 'progress' }, [
- Vue.h('div', { class: 'line', style: 'width: 60%' }, [
- Vue.h('div', '60%(课时)')
- ])
- ])
- ]),
- Vue.h('div', { class: 'other-info' }, [
- Vue.h('div', {}, [
- Vue.h(SsIcon, { name: "userGroup", size: "22px" }),
- Vue.h('div', {}, "《关于公司全面预算规划会议》"),
- ]),
- Vue.h('div', {}, [
- Vue.h(SsIcon, { name: "card", type: "common", size: "22px" }),
- Vue.h('div', {}, "一卡通余额:**************"),
- ]),
- Vue.h('div', {}, [
- Vue.h(SsIcon, { name: "site", type: "common", size: "22px" }),
- Vue.h('div', {}, "个人网站:xxx.xxx.xxx"),
- ]),
- ])
- ])
- ]
- })
- );
- }
- };
- // 待办
- export const TodoList = {
- name: 'TodoList',
- setup() {
- const todoGroupList = Vue.reactive([
- {
- title: "草稿",
- type: "folder",
- open: false,
- children: [
- { title: "被退回", time: "08/10 18:12" },
- { title: "项目立项申请", time: "08/10 18:12" },
- { title: "报销申请", time: "08/10 18:12" },
- { title: "资产领用申请", time: "08/10 18:12" },
- ],
- },
- {
- title: "被退回",
- type: "folder",
- open: true,
- children: [
- { title: "被退回", time: "08/10 18:12" },
- { title: "项目立项申请", time: "08/10 18:12" },
- { title: "报销申请", time: "08/10 18:12" },
- { title: "资产领用申请", time: "08/10 18:12" },
- ],
- },
- {
- title: "2020-高薪技术业项目申报",
- type: "file",
- children: [],
- },
- ]);
- const winInfo = Vue.ref(loadWinSize(winName.TodoList));
- const onSetting = () => {
- console.log("Clicked on settings button");
- };
- const onRefresh = () => {
- console.log("Clicked on refresh button");
- };
- const onSizeChange = (rect) => {
- console.log(rect)
- const style = {};
- for (const key in rect) {
- style[key] = rect[key] + "px";
- }
- saveWinSize(winName.TodoList, style);
- winInfo.value = style;
- };
- const onItemClick = (e) => {
- console.log("===>>>", e);
- };
- Vue.onMounted(() => {
- // console.log("TodoList component is mounted");
- });
- return () => Vue.h('div', { class: 'todo-list-container can-resize-box', style: winInfo.value },
- Vue.h(EditBox, { onSize: onSizeChange }, {
- default: () => [
- Vue.h('div', { class: 'header' }, [
- Vue.h(HeaderContainer, {
- title: "待办",
- icon: "todo",
- onSetting,
- onRefresh
- })
- ]),
- Vue.h('div', { class: 'body' }, [
- Vue.h(FolderContainer, { list: todoGroupList, onClick: onItemClick })
- ])
- ]
- })
- )
- }
- };
- // 催办
- export const UrgingList = {
- name: 'UrgingList',
- setup() {
- const todoGroupList = Vue.reactive([
- {
- title: "项目",
- type: "folder",
- open: true,
- children: [
- { title: "《高新技术企业认定》项目报销", time: "08/10 18:12" },
- { title: "日常办公报销", time: "08/10 18:12" },
- { title: "固定资产维修费用报销", time: "08/10 18:12" },
- ],
- },
- {
- title: "报销",
- type: "folder",
- open: true,
- children: [
- {
- title: "2020-企业-技术改造专项资金项目-立项申请",
- time: "08/10 18:12",
- },
- ],
- },
- {
- title: "2020-高薪技术企业项目申报",
- type: "file",
- children: [],
- },
- {
- title: "2020-高薪技术企业项目申报-技术改造专项资金项目-立项申请",
- type: "file",
- children: [],
- },
- ]);
- const winInfo = Vue.ref(loadWinSize(winName.UrgingList));
- const onSetting = () => {
- console.log("Clicked on settings button");
- };
- const onRefresh = () => {
- console.log("Clicked on refresh button");
- };
- const onSizeChange = (rect) => {
- const style = {};
- for (const key in rect) {
- style[key] = rect[key] + "px";
- }
- saveWinSize(winName.UrgingList, style);
- winInfo.value = style;
- };
- return () =>
- Vue.h('div', { class: 'todo-list-container can-resize-box', style: winInfo.value }, Vue.h(EditBox, { onSize: onSizeChange }, {
- default: () => [
- Vue.h('div', { class: 'header' }, [
- Vue.h(HeaderContainer, {
- title: "催办",
- icon: "alarm-clock",
- onSetting,
- onRefresh
- })
- ]),
- Vue.h('div', { class: 'body' }, [
- Vue.h(FolderContainer, { list: todoGroupList })
- ])
- ]
- })
- )
- }
- };
- // 公示公告
- export const Notice = {
- name: 'Notice',
- setup() {
- const list = Vue.reactive([
- { title: "关于2022年度中层干部的任免公告", time: "10/08 08:30" },
- { title: "2022年重组团队从心出发", time: "10/08 08:30" },
- { title: "关于单位进入粤港澳创新创业大赛决赛", time: "10/08 08:30" },
- { title: "关于2022年度中层干部的任免公告", time: "10/08 08:30" },
- ]);
- const winInfo = Vue.ref(loadWinSize(winName.Notice));
- const onSetting = () => {
- console.log("Clicked on settings button");
- };
- const onRefresh = () => {
- console.log("Clicked on refresh button");
- };
- const onSizeChange = (rect) => {
- const style = {};
- for (const key in rect) {
- style[key] = rect[key] + "px";
- }
- saveWinSize(winName.Notice, style);
- winInfo.value = style;
- };
- const SsIcon = Vue.resolveComponent('ss-icon');
- return () => Vue.h('div', { class: 'notice-list-container can-resize-box', style: winInfo.value }, [
- Vue.h(EditBox, { onSize: onSizeChange }, {
- default: () => [
- Vue.h('div', { class: 'header' }, [
- Vue.h(HeaderContainer, {
- title: "公示公告",
- icon: "notice",
- onSetting: onSetting,
- onRefresh: onRefresh
- })
- ]),
- Vue.h('div', { class: 'body' }, list.map((item, i) =>
- Vue.h('div', { key: i }, [
- Vue.h('div', [
- Vue.h(SsIcon, { name: "file", size: "22px" }),
- Vue.h('span', item.title)
- ]),
- Vue.h('div', item.time)
- ])
- ))
- ]
- })
- ]);
- }
- };
- // 快捷发起
- export const Launch = {
- name: 'Launch',
- setup() {
- const winInfo = Vue.ref(loadWinSize(winName.launch));
- const popupInfo = Vue.reactive({
- status: false,
- height: 100,
- width: 100,
- left: 0,
- top: 0,
- });
- const popupStyle = Vue.computed(() => {
- const { width, height, left, top } = popupInfo;
- return {
- left: `${left - (130 - width) / 2}px`,
- top: `${top + height}px`,
- };
- });
- const items = Vue.reactive([
- { name: "qingjia", text: "请假" },
- {
- name: "shoufukuan",
- text: "收付款",
- hasPopup: true,
- popupContent: [
- { text: "收款" },
- { text: "付款" }
- ]
- },
- {
- name: "kaoqin",
- text: "考勤",
- hasPopup: true,
- popupContent: [
- { text: "2" },
- { text: "3" }
- ]
- }
- ]);
- const onSetting = () => console.log("Clicked on settings button");
- const onRefresh = () => console.log("Clicked on refresh button");
- const onMouseMove = (e, item) => {
- console.log("鼠标进入了",item.text)
- showPopupDialog(e, item)
- };
- const onMouseleave = (e,item) => {
- console.log("鼠标离开了",item.text)
- popupInfo.status = false;
- };
- const showPopupDialog = (e, item) => {
- const dom = e.target.closest('.item');
- if (dom) {
- const rect = dom.getBoundingClientRect();
- popupInfo.height = rect.height;
- popupInfo.left = rect.left;
- popupInfo.top = rect.top;
- popupInfo.width = rect.width;
- setTimeout(() => {
- popupInfo.status = true;
- }, 600)
- }
- };
- const onSizeChange = (rect) => {
- const style = {};
- for (const key in rect) style[key] = rect[key] + "px";
- saveWinSize(winName.launch, style);
- winInfo.value = style;
- };
- // Vue.onMounted(() => console.log("LaunchContainer component is mounted"));
- const SsIcon = Vue.resolveComponent('ss-icon');
- return () => Vue.h('div', { class: 'launch-container can-resize-box', style: winInfo.value }, [
- Vue.h(EditBox, { onSize: onSizeChange }, {
- default: () => [
- Vue.h('div', { class: 'header' }, [
- Vue.h(HeaderContainer, { title: "快捷发起", icon: "lightning", onSetting, onRefresh })
- ]),
- Vue.h('div', { class: 'body' }, items.map(item =>
- Vue.h('div', { class: 'item', onMousemove: e => onMouseMove(e, item), onMouseleave: e => onMouseleave(e, item) }, [
- Vue.h(SsIcon, {
- class: item.hasPopup && item.popupContent.length > 0 ? "mark-down" : "",
- name: item.name,
- size: "36px"
- }),
- Vue.h('div', { class: 'text' }, item.text),
- item.hasPopup && popupInfo.status ? Vue.h('div', { class: 'popup', style: popupStyle.value }, item.popupContent.map(content =>
- Vue.h('div', content.text)
- )) : null
- ])
- ))
- ]
- })
- ])
- }
- };
- // 项目实时统计图
- export const Statistics = {
- name: 'Statistics',
- setup() {
- const winInfo = Vue.ref(loadWinSize(winName.Statistics));
- const chartInstance = Vue.ref(null);
- const option = {
- tooltip: {
- trigger: "axis",
- axisPointer: {
- type: "cross",
- crossStyle: {
- color: "#999",
- },
- },
- },
- legend: {
- data: ["项目金额", "成本"]
- },
- xAxis: [{
- type: "category",
- data: ["2月", "3月", "4���", "5月", "6月", "7月", "8月", "9月", "10月", "11月"],
- axisPointer: {
- type: "shadow"
- },
- }],
- yAxis: [{
- type: "value",
- name: "单位:万(RMB)",
- min: 0,
- max: 1000,
- interval: 200,
- axisLabel: {
- formatter: "{value} "
- },
- }, {
- type: "value",
- name: "成本",
- min: 0,
- max: 1000,
- interval: 200,
- axisLabel: {
- formatter: "{value}"
- },
- }],
- series: [{
- name: "项目金额",
- type: "bar",
- tooltip: {
- valueFormatter: function (value) {
- return value + " ml";
- }
- },
- data: [250, 850, 500, 650, 450, 850, 780, 450, 700, 850]
- }, {
- name: "成本",
- type: "line",
- yAxisIndex: 1,
- tooltip: {
- valueFormatter: function (value) {
- return value;
- }
- },
- data: [400, 390, 580, 590, 430, 420, 430, 550, 230, 250]
- }]
- }
- // 初始化echants
- const initChart = () => {
- const chartDom = document.getElementById("statistics-chart");
- if (!chartDom) return;
- chartInstance.value = echarts.init(chartDom);
- chartInstance.value.setOption(option);
- };
- const onSetting = () => console.log("Clicked on settings button");
- const onRefresh = () => {
- console.log("Clicked on refresh button");
- if (chartInstance.value) {
- chartInstance.value.clear();
- initChart(); // Reinitialize the chart to reflect any new data or settings
- }
- };
- const onSizeChange = (rect) => {
- const style = {};
- for (const key in rect) style[key] = rect[key] + "px";
- saveWinSize(winName.Statistics, style);
- winInfo.value = style;
- if (chartInstance.value) {
- chartInstance.value.resize();
- }
- };
- Vue.onMounted(() => {
- // console.log("StatisticsContainer component is mounted");
- initChart(); // Initialize the chart when component is mounted
- });
- // return { winInfo, onSetting, onRefresh, onSizeChange };
- return () => Vue.h('div', { class: 'statistics-container can-resize-box', style: winInfo.value }, [
- Vue.h(EditBox, { onSize: onSizeChange }, {
- default: () => [
- Vue.h('div', { class: 'header' }, [
- Vue.h(HeaderContainer, { title: "项目实时统计图", icon: "layer", onSetting, onRefresh })
- ]),
- Vue.h('div', { class: 'body' }, [
- Vue.h('div', { id: 'statistics-chart', class: 'chart-container' })
- ])
- ]
- })
- ]);
- },
- };
|