基于 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改为import {ljAxios} from "loong-component";
即可
类型:【Object】
可在全局引入时定义全局的组件参数,该参数定义权重较低,可在单独页面使用中通过对象绑定进行覆盖
类型:【Object】
类型:【String】
文件上传地址
类型:【String】
文件上传请求时,文件传输的键,默认为file
类型:【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) {
}
}
类型:【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对象
}
类型:【Object】
类型:【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组件使用文档 :::
类型:【Function】
列表数据渲染方法,其返回参数为接口请求返回体(经过axios过滤后),需抛出页面所需的total及list
parseData: (res) => {
return {
total: res.total,
list: res.list
}
}
类型:【Object】
完美支持element-UI中表格的所有属性 ,其中属性名已转化为小驼峰形式
table: {
size: "small",
border: true,
rowStyle: () => {
return {
fontSize: '14px'
}
},
headerCellStyle: () => {
return {
fontSize: '14px'
}
},
listType: "card-apart",//可选值 card-apart|simple,推荐使用前者.前者将查询栏、操作栏、表格栏清晰分层.案件产品目前使用该配置
searchAlone: true
}
},
类型:【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)
}
}
}
类型:【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);