npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

loong-component

v0.11.24-rc.9

Published

龙进组件库

Downloads

629

Readme

Loong-Component 组件库

cover

基于 Vue + ElementUI

介绍:

  • 借助快速搭建平台能够快速生成页面;
  • 内置常用一些业务组件,例如列表、编辑表单、查看表单、弹窗等等;
  • 基于elementUI组件二次封装,将参数进行规范统一;
  • 支持动态插槽、绑定事件等;
  • 等等

开发指南

引入与全局配置

安装

yarn add loong-component --save

全局引入

loong-component组件自带了许多常用功能,包括axios的封装引入,element-UI的一些常用组件.

import Vue from "Vue" ;
import loong,{elementUI} from "loong-component";//elementUI引入以及其他公共函数引入
import "loong-component/lib/loong-component.css";

Vue.use(loong,{options})// 引入组件,并进行全局默认配置
Vue.use(elementUI);//项目用了loong组件库,可不需在单独安装elementUI

全局axios封装

组件中统一使用的axios,为了和系统中的axios统一,可以对同一个axios进行统一封装拦截
将原来引用的axios改为import {ljAxios} from "loong-component";即可

options配置参数api:

类型:【Object】
可在全局引入时定义全局的组件参数,该参数定义权重较低,可在单独页面使用中通过对象绑定进行覆盖

options.inputConfig 输入组件配置

类型:【Object】

options.inputConfig.action

类型:【String】
文件上传地址

options.inputConfig.paramName

类型:【String】
文件上传请求时,文件传输的键,默认为file

options.inputConfig.renderFile

类型:【Function】
文件渲染的请求方法,异步方法,返回值为url,name。url需要可以在img标签的src中直接展示
参数:data为当前文件对象

renderFile: async (data) => {
   try {
       if (typeof data === 'string') {
           data = await getAttachment(data);
       }
       let url;
       let ext = "bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,wmf,webp,avif,apng";
       if (ext.indexOf(data.extension.toLowerCase()) > -1) {
           let responseData = await downloadFile(data.downPath);
           let blob = new Blob([responseData.data])
           url = window.URL.createObjectURL(blob);
           console.log('url :>> ', url);
       }
       return {
           url: url || "??",
           name: `${data.fileName}.${data.extension}`,
           ext: data.extension,
           _file: data
       }
    } catch (e) {
    }
}

options.inputConfig.downloadFile

类型:【Function】
文件下载方法,可定义下载的请求方法
参数:data=文件列表当前对象

async downloadFile(data) {
        let responseData = await downLoadFile({fileId: data.id, fileName: data.name, d: 0}, 'blob');
        let blob = new Blob([responseData.data])
        let downloadElement = document.createElement('a')
        let href = window.URL.createObjectURL(blob); //创建下载的链接
        downloadElement.href = href;
        downloadElement.download = data.name; //下载后文件名
        document.body.appendChild(downloadElement);
        downloadElement.click(); //点击下载
        document.body.removeChild(downloadElement); //下载完成移除元素
        window.URL.revokeObjectURL(href); //释放blob对象
    }

options.list 列表相关配置

类型:【Object】

options.list.renderDataFiltersItems

类型:【Function】
列表数据权限统一管理,对单位、部门、人员进行不同权限数据过滤展示,实现对数据进行过滤. 可参考[网办系统-案件列表]

 async renderDataFiltersItems: (listVue) => {
            const _this = listVue.$parent;
            const user = listVue.$store.state.user;//id depId unitId
            let page = 1;
            let items = [
                {
                    // labelWidth: "60px",
                    elementType: "select",
                    name: "unitId",
                    value: "",
                    clearable: true,
                    filterable: true,
                    options: [],
                    label: "单位",
                    placeholder: "请选择单位",
                    span: 6,
                    width: "100%",
                    event: {
                        change: (e) => {
                            getDepTreeData(e);
                        }
                    }
                },
                {
                    labelWidth: "80px",
                    elementType: "treeSelect",
                    value: [],
                    noOptionsText: '暂无部门数据',
                    placeholder: "请选择部门",
                    label: "部门",
                    name: "depIds",
                    defaultExpandLevel: 999,
                    options: [],
                    clearable: true,
                    zIndex: 3001,
                    appendToBody: true,
                    multiple: true,
                    limit: 1,
                    showCount: true,
                    noChildrenText: "当前无下级部门",
                    // branchNodesFirst: true,
                    valueConsistsOf: "ALL",
                    limitText: (count) => {
                        return `+${count}`
                    },
                    normalizer: (d) => {
                        return {
                            ...d,
                            children: d.sub === null ? [] : d.sub,
                            value: d.id,
                            label: d.name,
                        }
                    },
                    span: 7,
                    width: "100%",
                    event: {
                        input: (value, instanceId) => {
                            page = 1;
                            getLoginUserAvailableUser(value)
                        }
                    }
                },
                {
                    // labelWidth: "60px",
                    elementType: "select",
                    name: "userId",
                    value: "",
                    selectLoadMore: () => {
                        page++;
                        const depItemValue = _this.search.formData.formItems.find(s => s.name === 'depIds')?.value;
                        getLoginUserAvailableUser(depItemValue);
                    },
                    clearable: true,
                    filterable: true,
                    options: [],
                    label: "人员",
                    placeholder: "请选择人员",
                    span: 5,
                    width: "100%",
                },
            ];
            if (_this.search.dataFiltersOverrideItems && _this.search.dataFiltersOverrideItems.length > 0) {
                items = items.map(item => {
                    const overrideItem = _this.search.dataFiltersOverrideItems.find(s => s.name === item.name);
                    if (overrideItem) {
                        item = {
                            ...item,
                            ...overrideItem
                        }
                    }
                    return item
                });
                items = items.filter(s => typeof s.show === 'undefined' || s.show);
            }

            if (_this.search.dataFiltersOverrideCallback && typeof _this.search.dataFiltersOverrideCallback === 'function') {
                items = _this.search.dataFiltersOverrideCallback({
                    items
                });
            }
            _this.search.formData.formItems.unshift(...items);

            //获取部门数据
            let getDepTreeData = async (unitId = null) => {
                if (!_this.search.formData.formItems.find(s => s.name === 'depIds'))
                    return;
                const depsData = await getLoginUserAvailableDepForTreeOptionApi({unitId});
                _this.search.formData.formItems.setOptions("depIds", depsData);
                if (depsData && depsData.length > 0) {
                    let result;
                    if (_this.search.formData.formItems.find(s => s.name === 'depIds')?.multiple) {
                        result = [depsData[0].id];
                        let data = depsData[0];
                        utils.findTreeData(data.sub, result, "id", "sub");
                    } else {
                        result = depsData[0].id;
                    }

                    _this.search.formData.formItems.setValue("depIds", result).setValue('depIds', result, 'name', 'default');

                    //如果部门返回有仅一条数据并且还是当前登录用户的部门时,默认隐藏
                    if (result.length === 1 && depsData[0].id === user.depId && depsData[0].id !== 'all') {
                        _this.search.formData.formItems.setShow("depIds", false);
                    }
                    await getLoginUserAvailableUser(result)
                } else {
                    _this.search.formData.formItems.setValue("depIds", []).setValue('depIds', [], 'name', 'default');
                    _this.search.formData.formItems.setShow("depIds", false);
                    await getLoginUserAvailableUser([])
                }
            };

            //获取当前登录用户可访问用户下拉数据
            let getLoginUserAvailableUser = async (depIds) => {
                if (!_this.search.formData.formItems.find(s => s.name === 'userId'))
                    return;
                let params = {
                    depIds,
                    page: page,
                    limit: 20,
                    unitId: _this.search.formData.formItems.find(s => s.name === 'unitId')?.value
                };
                const userData = await getLoginUserAvailableUserForOptionApi(params);
                const userOption = _this.search.formData.formItems.find(s => s.name === 'userId')?.options;
                let newOptions = userData.list.map(s => {
                    return {label: s.name, value: s.id}
                });
                _this.search.formData.formItems.setOptions("userId", page === 1 ? newOptions : [...userOption, ...newOptions]);
                if (page === 1) {
                    if (newOptions && newOptions.length > 0) {
                        //如果用户返回有仅一条数据并且还是当前登录用户时,默认隐藏
                        if (newOptions.length === 1 && newOptions[0].value === user.id) {
                            _this.search.formData.formItems.setShow("userId", false);
                        }
                        _this.search.formData.formItems.setValue("userId", newOptions[0].value).setValue('userId', newOptions[0].value, 'name', 'default');
                    } else {
                        _this.search.formData.formItems.setValue("userId", "");
                        _this.search.formData.formItems.setShow("userId", false);
                    }
                }
            };

            //初始化
            const unitData = await getLoginUserAvailableUnitForOptionApi({});
            if (unitData && unitData.length > 0) {
                _this.search.formData.formItems.setValue("unitId", unitData[0].id).setValue('unitId', unitData[0].id, 'name', 'default');
                _this.search.formData.formItems.setOptions("unitId", unitData.map(s => {
                    return {label: s.name, value: s.id}
                }))
                let defaultUnitId = unitData[0].id;
                if (unitData.length === 1 && defaultUnitId === user.unitId) {
                    _this.search.formData.formItems.setShow("unitId", false);
                }
                await getDepTreeData(defaultUnitId)
            } else {
                _this.search.formData.formItems.setShow("unitId", false);
                await getDepTreeData()
            }

            listVue.searchLoad = true;
        }

需要此功能需要在指定页面lj-list组件配置,如下:

<template>
<lj-list use-data-filters></lj-list>
</template>

::: tip 提示 如果需要对过滤下拉框下拉框名称以及不展示等详细配置见lj-list组件使用文档 :::

options.list.parseData

类型:【Function】
列表数据渲染方法,其返回参数为接口请求返回体(经过axios过滤后),需抛出页面所需的total及list

 parseData: (res) => {
            return {
                total: res.total,
                list: res.list
            }
        }

options.list.table

类型:【Object】
完美支持element-UI中表格的所有属性 ,其中属性名已转化为小驼峰形式

table: {
        size: "small",
        border: true,
        rowStyle: () => {
            return {
                fontSize: '14px'
            }
        },
        headerCellStyle: () => {
            return {
                fontSize: '14px'
            }
        },
        listType: "card-apart",//可选值 card-apart|simple,推荐使用前者.前者将查询栏、操作栏、表格栏清晰分层.案件产品目前使用该配置
        searchAlone: true
    }
},

options.permission 权限

类型:【Function】
参数:【el:元素dom】【bind:参数对象】
页面元素的权限控制,如果不满足条件可自行对dom进行删除等动作

1.使用了loong-component组件的,组件内部已经加了v-permission,需要在控制元素的对象中添加permissionCode属性作为权限标识
2.其他元素权限控制需在标签上加上 v-permission="yourPermissionCode"

permission(el, bind) {
    const { value } = bind;
    if (value) {
        // 权限值 需要在系统中心的权限管理添加并在角色管理中配置才生效
        const permissionList = store.state?.permissionList;
        // all表示带有管理员权限
        if (permissionList.find(n => n.code === 'all')) {
            return;
        }
        const routerName = router.app._route.name;
        // 根据路由name来区分页面权限
        const target = permissionList.find(item => (item.code === routerName));
        let permissionValue = new Set();
        if (typeof value === 'string') {
            permissionValue = new Set(value.split(","));
        } else if (typeof value === 'object' && Array.isArray(value)) {
            permissionValue = new Set(value);
        }
        const hasPermission = target?.visibleElementCodes.some(s => permissionValue.has(s));
        if (!hasPermission) {
            el.parentNode && el.parentNode.removeChild(el)
        }
    }
}

options.store 状态管理

类型:【Function】
直接传入项目store,组件内部弹窗内部后期可对项目store进行管理及操作.

完整示例

部分函数及接口定义可钉我(@hzw)获取


//引入
import loongComponent, {elementUI} from 'loong-component'
import 'loong-component/lib/loong-component.css'

const inputConfig = {
    action: uploadFileSrc,//上传地址
    paramName: "webFile",//上传请求键,默认为file
    //转换上传回来的数据
    parseData: (data) => {
        let res = data.data;
        //未登录或登录超时,需要重新登录
        switch (res.code) {
            case setting.responseCode.ok://操作成功
                return data.data.data
            case setting.responseCode.noLogin://需要重新登录(跳转)
            case setting.responseCode.sessionTimeout://需要续签 TODO 暂时不处理
                toLogin();
                break;
            default:
                message.error(res.msg);
                toLogin();
                break;
        }
    },
    //渲染数据
    renderFile: async (data) => {
        try {
            if (typeof data === 'string') {
                data = await getAttachment(data);
            }
            let url;
            let ext = "bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,wmf,webp,avif,apng";
            if (ext.indexOf(data.extension.toLowerCase()) > -1) {
                let responseData = await downloadFile(data.downPath);
                let blob = new Blob([responseData.data])
                url = window.URL.createObjectURL(blob);
                console.log('url :>> ', url);
            }
            return {
                url: url || "??",
                name: `${data.fileName}.${data.extension}`,
                ext: data.extension,
                _file: data
            }
        } catch (e) {
        }
    },
    // //下载文件
    async downloadFile(data) {
        const responseData = await downloadFile(data._file.downPath);
        const blob = new Blob([responseData.data])
        const href = window.URL.createObjectURL(blob);
        let downloadElement = document.createElement('a')
        downloadElement.href = href;
        downloadElement.download = data.name; //下载后文件名
        document.body.appendChild(downloadElement);
        downloadElement.click(); //点击下载
        document.body.removeChild(downloadElement); //下载完成移除元素
        window.URL.revokeObjectURL(href); //释放blob对象
    }
}


Vue.use(loongComponent, {
    //列表相关配置
    list: {
        /**
         * 数据权限过滤
         * @param listVue
         * @return {Promise<void>}
         */
        async renderDataFiltersItems(listVue) {
            const _this = listVue.$parent;
            const user = listVue.$store.state.user;//id depId unitId
            let page = 1;
            let items = [
                {
                    // labelWidth: "60px",
                    elementType: "select",
                    name: "unitId",
                    value: "",
                    clearable: true,
                    filterable: true,
                    options: [],
                    label: "单位",
                    placeholder: "请选择单位",
                    span: 6,
                    width: "100%",
                    event: {
                        change: (e) => {
                            getDepTreeData(e);
                        }
                    }
                },
                {
                    labelWidth: "80px",
                    elementType: "treeSelect",
                    value: [],
                    noOptionsText: '暂无部门数据',
                    placeholder: "请选择部门",
                    label: "部门",
                    name: "depIds",
                    defaultExpandLevel: 999,
                    options: [],
                    clearable: true,
                    zIndex: 3001,
                    appendToBody: true,
                    multiple: true,
                    limit: 1,
                    showCount: true,
                    noChildrenText: "当前无下级部门",
                    // branchNodesFirst: true,
                    valueConsistsOf: "ALL",
                    limitText: (count) => {
                        return `+${count}`
                    },
                    normalizer: (d) => {
                        return {
                            ...d,
                            children: d.sub === null ? [] : d.sub,
                            value: d.id,
                            label: d.name,
                        }
                    },
                    span: 7,
                    width: "100%",
                    event: {
                        input: (value, instanceId) => {
                            page = 1;
                            getLoginUserAvailableUser(value)
                        }
                    }
                },
                {
                    // labelWidth: "60px",
                    elementType: "select",
                    name: "userId",
                    value: "",
                    selectLoadMore: () => {
                        page++;
                        const depItemValue = _this.search.formData.formItems.find(s => s.name === 'depIds')?.value;
                        getLoginUserAvailableUser(depItemValue);
                    },
                    clearable: true,
                    filterable: true,
                    options: [],
                    label: "人员",
                    placeholder: "请选择人员",
                    span: 5,
                    width: "100%",
                },
            ];
            if (_this.search.dataFiltersOverrideItems && _this.search.dataFiltersOverrideItems.length > 0) {
                items = items.map(item => {
                    const overrideItem = _this.search.dataFiltersOverrideItems.find(s => s.name === item.name);
                    if (overrideItem) {
                        item = {
                            ...item,
                            ...overrideItem
                        }
                    }
                    return item
                });
                items = items.filter(s => typeof s.show === 'undefined' || s.show);
            }

            if (_this.search.dataFiltersOverrideCallback && typeof _this.search.dataFiltersOverrideCallback === 'function') {
                items = _this.search.dataFiltersOverrideCallback({
                    items
                });
            }
            _this.search.formData.formItems.unshift(...items);

            //获取部门数据
            let getDepTreeData = async (unitId = null) => {
                if (!_this.search.formData.formItems.find(s => s.name === 'depIds'))
                    return;
                const depsData = await getLoginUserAvailableDepForTreeOptionApi({unitId});
                _this.search.formData.formItems.setOptions("depIds", depsData);
                if (depsData && depsData.length > 0) {
                    let result;
                    if (_this.search.formData.formItems.find(s => s.name === 'depIds')?.multiple) {
                        result = [depsData[0].id];
                        let data = depsData[0];
                        utils.findTreeData(data.sub, result, "id", "sub");
                    } else {
                        result = depsData[0].id;
                    }

                    _this.search.formData.formItems.setValue("depIds", result).setValue('depIds', result, 'name', 'default');

                    //如果部门返回有仅一条数据并且还是当前登录用户的部门时,默认隐藏
                    if (result.length === 1 && depsData[0].id === user.depId && depsData[0].id !== 'all') {
                        _this.search.formData.formItems.setShow("depIds", false);
                    }
                    await getLoginUserAvailableUser(result)
                } else {
                    _this.search.formData.formItems.setValue("depIds", []).setValue('depIds', [], 'name', 'default');
                    _this.search.formData.formItems.setShow("depIds", false);
                    await getLoginUserAvailableUser([])
                }
            };

            //获取当前登录用户可访问用户下拉数据
            let getLoginUserAvailableUser = async (depIds) => {
                if (!_this.search.formData.formItems.find(s => s.name === 'userId'))
                    return;
                let params = {
                    depIds,
                    page: page,
                    limit: 20,
                    unitId: _this.search.formData.formItems.find(s => s.name === 'unitId')?.value
                };
                const userData = await getLoginUserAvailableUserForOptionApi(params);
                const userOption = _this.search.formData.formItems.find(s => s.name === 'userId')?.options;
                let newOptions = userData.list.map(s => {
                    return {label: s.name, value: s.id}
                });
                _this.search.formData.formItems.setOptions("userId", page === 1 ? newOptions : [...userOption, ...newOptions]);
                if (page === 1) {
                    if (newOptions && newOptions.length > 0) {
                        //如果用户返回有仅一条数据并且还是当前登录用户时,默认隐藏
                        if (newOptions.length === 1 && newOptions[0].value === user.id) {
                            _this.search.formData.formItems.setShow("userId", false);
                        }
                        _this.search.formData.formItems.setValue("userId", newOptions[0].value).setValue('userId', newOptions[0].value, 'name', 'default');
                    } else {
                        _this.search.formData.formItems.setValue("userId", "");
                        _this.search.formData.formItems.setShow("userId", false);
                    }
                }
            };

            //初始化
            const unitData = await getLoginUserAvailableUnitForOptionApi({});
            if (unitData && unitData.length > 0) {
                _this.search.formData.formItems.setValue("unitId", unitData[0].id).setValue('unitId', unitData[0].id, 'name', 'default');
                _this.search.formData.formItems.setOptions("unitId", unitData.map(s => {
                    return {label: s.name, value: s.id}
                }))
                let defaultUnitId = unitData[0].id;
                if (unitData.length === 1 && defaultUnitId === user.unitId) {
                    _this.search.formData.formItems.setShow("unitId", false);
                }
                await getDepTreeData(defaultUnitId)
            } else {
                _this.search.formData.formItems.setShow("unitId", false);
                await getDepTreeData()
            }

            listVue.searchLoad = true;
        },
        parseData: ({list, total}) => {
            return {
                total: total,
                list: list
            }
        },
        table: {
            size: "small",
            border: true,
            rowStyle: () => {
                return {
                    fontSize: '14px'
                }
            },
            headerCellStyle: () => {
                return {
                    fontSize: '14px'
                }
            },
        },
        listType: "card-apart",
        searchAlone: true
    },
    inputConfig,
    store: store,
    //权限控制
    //1.使用了loong-component组件的,组件内部已经加了v-permission,无需单独添加,但需加 permissionCode 作为权限标识
    //2.未使用loong-component的,自己需要在标签上添加v-permission自定义属性
    permission(el, bind) {
        const {value} = bind;
        if (value) {
            // 权限值 需要在系统中心的权限管理添加并在角色管理中配置才生效
            const permissionList = store.state?.permissionList;
            // all表示带有管理员权限
            if (permissionList.find(n => n.code === 'all')) {
                return;
            }
            const routerName = router.app._route.name;
            // 根据路由name来区分页面权限
            const target = permissionList.find(item => (item.code === routerName));
            let permissionValue = new Set();
            if (typeof value === 'string') {
                permissionValue = new Set(value.split(","));
            } else if (typeof value === 'object' && Array.isArray(value)) {
                permissionValue = new Set(value);
            }
            const hasPermission = target?.visibleElementCodes.some(s => permissionValue.has(s));
            if (!hasPermission) {
                el.parentNode && el.parentNode.removeChild(el)
            }
        }
    }
});
Vue.use(elementUI);