| | |
| | | import {refreshTabView, modifyTabview} from '@/store/action' |
| | | |
| | | import MainTable from '@/tabviews/zshare/normalTable' |
| | | import MainAction from '@/tabviews/zshare/actionList' |
| | | import VerifyCard from '@/tabviews/zshare/verifycard' |
| | | import MainSearch from '@/tabviews/zshare/topSearch' |
| | | import NotFount from '@/components/404' |
| | | import './index.scss' |
| | | |
| | | const VerifyCard = asyncLoadComponent(() => import('@/tabviews/zshare/verifycard')) |
| | | const MainAction = asyncLoadComponent(() => import('@/tabviews/zshare/actionList')) |
| | | const SubTable = asyncLoadComponent(() => import('@/tabviews/subtable')) |
| | | const SubTabTable = asyncLoadComponent(() => import('@/tabviews/subtabtable')) |
| | | const FormTab = asyncLoadComponent(() => import('@/tabviews/formtab')) |
| | |
| | | |
| | | class NormalTable extends Component { |
| | | static propTpyes = { |
| | | param: PropTypes.any, // 其他页面传递的搜索条件等参数 |
| | | MenuID: PropTypes.string, // 菜单Id |
| | | MenuNo: PropTypes.string, // 菜单参数 |
| | | MenuName: PropTypes.string, // 菜单参数 |
| | | MenuID: PropTypes.string // 菜单Id |
| | | MenuName: PropTypes.string // 菜单名称 |
| | | } |
| | | |
| | | state = { |
| | |
| | | viewlost: false, // 页面丢失:1、未获取到配置-页面丢失;2、页面未启用 |
| | | lostmsg: '', // 页面丢失时的提示信息 |
| | | config: {}, // 页面配置信息,包括按钮、搜索、显示列、标签等 |
| | | userConfig: null, // 用户自定义设置 |
| | | userParam: null, // 保存用户编辑中的配置 |
| | | searchlist: null, // 搜索条件 |
| | | actions: null, // 按钮集 |
| | | columns: null, // 显示列 |
| | |
| | | tabParam: null, // 表单标签参数 |
| | | refreshtabs: null, // 需要刷新的标签集 |
| | | confirmLoading: false,// 自定义设置模态框加载中 |
| | | settingVisible: false // 自定义设置模态框 |
| | | revertLoading: false, // 恢复默认设置 |
| | | settingVisible: false,// 自定义设置模态框 |
| | | triggerBtn: null, // 点击表格中或快捷键触发的按钮 |
| | | tabActive: null // 标签页展开控制 |
| | | } |
| | | |
| | | /** |
| | | * @description 获取页面配置信息 |
| | | */ |
| | | async loadconfig () { |
| | | const { permAction } = this.props |
| | | const { permAction, permMenus, param } = this.props |
| | | |
| | | let param = { |
| | | let _param = { |
| | | func: 'sPC_Get_LongParam', |
| | | MenuID: this.props.MenuID |
| | | } |
| | | let result = await Api.getSystemCacheConfig(param) |
| | | let result = await Api.getSystemCacheConfig(_param) |
| | | |
| | | if (result.status) { |
| | | let config = '' |
| | | let userConfig = '' |
| | | let userConfig = null |
| | | let _curUserConfig = '' |
| | | |
| | | try { // 配置信息解析 |
| | | config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) |
| | |
| | | if (result.LongParamUser) { |
| | | try { // 配置信息解析 |
| | | userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser))) |
| | | _curUserConfig = userConfig[this.props.MenuID] |
| | | } catch (e) { |
| | | console.warn('Parse Failure') |
| | | userConfig = '' |
| | | userConfig = null |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | }) |
| | | |
| | | if (userConfig) { |
| | | config.setting = {...config.setting, ...userConfig.setting} |
| | | let _actions = {} |
| | | userConfig.action.forEach(item => { |
| | | _actions[item.uuid] = item |
| | | }) |
| | | if (_curUserConfig) { |
| | | config.setting = {...config.setting, ..._curUserConfig.setting} |
| | | config.easyCode = _curUserConfig.easyCode || config.easyCode || '' |
| | | |
| | | config.action = config.action.map(item => { |
| | | if (_actions[item.uuid]) { |
| | | item = {...item, ..._actions[item.uuid]} |
| | | if (item.execMode) { |
| | | item.OpenType = 'funcbutton' |
| | | } |
| | | |
| | | if (_curUserConfig.action[item.uuid]) { |
| | | delete _curUserConfig.action[item.uuid].label |
| | | item = {...item, ..._curUserConfig.action[item.uuid]} |
| | | } |
| | | |
| | | if (item.OpenType === 'funcbutton' && item.funcType === 'print' && item.verify && item.printer) { |
| | | item.verify.defaultPrinter = item.printer.defaultPrinter || '' |
| | | if (item.verify.printerTypeList && item.printer.printerList) { |
| | | item.verify.printerTypeList = item.verify.printerTypeList.map(cell => { |
| | | cell.printer = item.printer.printerList[cell.Value] || '' |
| | | |
| | | return cell |
| | | }) |
| | | } |
| | | } |
| | | |
| | | return item |
| | | }) |
| | | } |
| | | |
| | | let _tabActive = {} // 筛选展开的tab页 |
| | | |
| | | config.tabgroups.forEach(groupId => { |
| | | if (!config[groupId] || config[groupId].length === 0) return |
| | | _tabActive[groupId] = config[groupId][0].uuid |
| | | }) |
| | | |
| | | let _arrField = [] // 字段集 |
| | | let _columns = [] // 显示列 |
| | |
| | | } |
| | | }) |
| | | |
| | | if (config.gridBtn && config.gridBtn.display && _operations.length > 0) { |
| | | _columns.push({ |
| | | ...config.gridBtn, |
| | | operations: _operations |
| | | }) |
| | | } |
| | | |
| | | |
| | | // 1、筛选字段集,2、过滤隐藏列及合并列中的字段uuid |
| | | config.columns.forEach(col => { |
| | | if (col.field) { |
| | |
| | | }) |
| | | |
| | | // 生成显示列,处理合并列中的字段 |
| | | config.columns.forEach(col => { |
| | | config.columns.forEach((col, index) => { |
| | | if (_hideCol.includes(col.uuid)) return |
| | | |
| | | if (col.linkThdMenu && !permMenus[col.linkThdMenu.MenuID]) { |
| | | col.linkThdMenu = '' |
| | | } |
| | | |
| | | col.sort = index |
| | | |
| | | if (col.type === 'colspan' && col.sublist) { |
| | | let _col = JSON.parse(JSON.stringify(col)) |
| | |
| | | } |
| | | }) |
| | | |
| | | let valid = true // 搜索条件必填验证 |
| | | config.search.forEach(field => { |
| | | if (field.required === 'true' && !field.initval) { |
| | | if (config.gridBtn && config.gridBtn.display && _operations.length > 0) { |
| | | _columns.push({ |
| | | ...config.gridBtn, |
| | | operations: _operations |
| | | }) |
| | | } |
| | | |
| | | let valid = true // 搜索条件必填验证, 初始搜索条件 |
| | | let initSearch = config.search.map(item => { |
| | | let _item = JSON.parse(JSON.stringify(item)) |
| | | if (_item.type === 'text' && param && param.searchkey === _item.field) { |
| | | _item.initval = param.searchval |
| | | } |
| | | |
| | | if (_item.required === 'true' && !_item.initval) { |
| | | valid = false |
| | | } |
| | | return _item |
| | | }) |
| | | |
| | | if (_curUserConfig) { |
| | | _columns = _columns.map(item => { |
| | | if (_curUserConfig.columns[item.uuid]) { |
| | | delete _curUserConfig.columns[item.uuid].label |
| | | item = {...item, ..._curUserConfig.columns[item.uuid]} |
| | | } |
| | | |
| | | return item |
| | | }) |
| | | |
| | | _columns.sort((pre, next) => { |
| | | return pre.sort - next.sort |
| | | }) |
| | | } |
| | | |
| | | this.setState({ |
| | | loadingview: false, |
| | | config: config, |
| | | tabActive: _tabActive, |
| | | userConfig: userConfig, |
| | | setting: config.setting, |
| | | searchlist: config.search, |
| | | actions: _actions, |
| | | columns: _columns, |
| | | logcolumns: _logcolumns, |
| | | arr_field: _arrField.join(','), |
| | | search: Utils.initMainSearch(config.search) // 搜索条件初始化(含有时间格式,需要转化) |
| | | search: Utils.initMainSearch(initSearch) // 搜索条件初始化(含有时间格式,需要转化) |
| | | }, () => { |
| | | this.improveSearch() |
| | | if (config.setting.onload !== 'false' && valid) { // 初始化可加载 |
| | |
| | | notification.warning({ |
| | | top: 92, |
| | | message: result.message, |
| | | duration: 10 |
| | | duration: 5 |
| | | }) |
| | | } |
| | | } |
| | | |
| | | setShortcut = () => { |
| | | const { actions } = this.state |
| | | if (!actions) return |
| | | const { actions, userConfig, config } = this.state |
| | | if (!userConfig) return |
| | | |
| | | document.onkeydown = (event) => { |
| | | let e = event || window.event |
| | |
| | | } else if (e.altKey) { |
| | | preKey = 'alt' |
| | | } |
| | | |
| | | |
| | | if (!preKey) return |
| | | |
| | | let istrigger = false |
| | | |
| | | actions.forEach(item => { |
| | | if (!item.shortcut || typeof(item.shortcut) !== 'object' || item.shortcut.length === 0 || istrigger) return |
| | | |
| | |
| | | e.preventDefault() |
| | | istrigger = true |
| | | |
| | | this.refs.mainButton.actionTrigger(item) |
| | | this.setState({ |
| | | triggerBtn: { |
| | | uuid: new Date().getTime(), |
| | | parentId: this.props.MenuID, |
| | | button: item, |
| | | data: null |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | |
| | | if (istrigger) return |
| | | |
| | | Object.keys(userConfig).forEach(key => { |
| | | if (key === this.props.MenuID || !userConfig[key].action || istrigger) return |
| | | |
| | | let _actions = userConfig[key].action |
| | | |
| | | Object.keys(_actions).forEach(btnkey => { |
| | | let item = _actions[btnkey] |
| | | |
| | | if (!item.shortcut || typeof(item.shortcut) !== 'object' || item.shortcut.length === 0 || istrigger) return |
| | | |
| | | if (preKey === item.shortcut[0] && keyCode === item.shortcut[1]) { |
| | | e.preventDefault() |
| | | istrigger = true |
| | | |
| | | let _groupId = '' |
| | | let _ActiveTabId = '' |
| | | config.tabgroups.forEach(groupId => { |
| | | if (!config[groupId] || config[groupId].length === 0) return |
| | | |
| | | let _tab = config[groupId].filter(tab => tab.uuid === key)[0] |
| | | if (_tab) { |
| | | _groupId = groupId |
| | | _ActiveTabId = _tab.uuid |
| | | } |
| | | }) |
| | | |
| | | if (this.state.tabActive[_groupId] === _ActiveTabId) { |
| | | this.setState({ |
| | | triggerBtn: { |
| | | uuid: new Date().getTime(), |
| | | parentId: key, |
| | | button: {...item, uuid: btnkey}, |
| | | data: null |
| | | } |
| | | }) |
| | | } else { |
| | | this.setState({ |
| | | tabActive: {...this.state.tabActive, [_groupId]: _ActiveTabId} |
| | | }, () => { |
| | | this.setState({ |
| | | triggerBtn: { |
| | | uuid: new Date().getTime(), |
| | | parentId: key, |
| | | button: {...item, uuid: btnkey}, |
| | | data: null |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | } |
| | |
| | | notification.warning({ |
| | | top: 92, |
| | | message: item.label + ': ' + this.state.dict['main.datasource.settingerror'], |
| | | duration: 10 |
| | | duration: 5 |
| | | }) |
| | | } |
| | | }) |
| | |
| | | notification.warning({ |
| | | top: 92, |
| | | message: res.search.label + ':' + res.message, |
| | | duration: 10 |
| | | duration: 5 |
| | | }) |
| | | } |
| | | }) |
| | |
| | | notification.error({ |
| | | top: 92, |
| | | message: result.message, |
| | | duration: 15 |
| | | duration: 10 |
| | | }) |
| | | } |
| | | } |
| | |
| | | if (setting.interType === 'inner') { |
| | | param.func = setting.innerFunc |
| | | } else { |
| | | if (setting.sysInterface === 'true') { |
| | | param.rduri = window.GLOB.mainSystemApi || window.GLOB.subSystemApi |
| | | } else { |
| | | if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) { |
| | | param.rduri = window.GLOB.mainSystemApi |
| | | } else if (setting.sysInterface !== 'true') { |
| | | param.rduri = setting.interface |
| | | } |
| | | |
| | | param.appkey = window.GLOB.appkey || '' // 调用外部接口增加appkey |
| | | |
| | | if (setting.outerFunc) { |
| | | param.func = setting.outerFunc |
| | |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '未设置显示列!', |
| | | duration: 10 |
| | | duration: 5 |
| | | }) |
| | | return null |
| | | } |
| | |
| | | let param = { |
| | | func: 'sPC_Get_TableData', |
| | | obj_name: 'data', |
| | | arr_field: arr_field, |
| | | appkey: window.GLOB.appkey || '' |
| | | arr_field: arr_field |
| | | } |
| | | |
| | | let _orderBy = orderBy || setting.order |
| | |
| | | } |
| | | |
| | | if (setting.queryType === 'statistics') { // 统计数据源,内容替换 |
| | | let fieldmap = new Map() |
| | | let options = search.map(item => { |
| | | let _field = item.key |
| | | let allSearch = Utils.getAllSearchOptions(search) |
| | | |
| | | if (fieldmap.has(_field)) { |
| | | _field = _field + '1' |
| | | } |
| | | |
| | | fieldmap.set(item.key, true) |
| | | |
| | | let options = allSearch.map(item => { |
| | | return { |
| | | reg: new RegExp('@' + _field, 'ig'), |
| | | reg: new RegExp('@' + item.key + '@', 'ig'), |
| | | value: item.value |
| | | } |
| | | }) |
| | | |
| | | options.reverse() |
| | | |
| | | options.forEach(item => { |
| | | _dataresource = _dataresource.replace(item.reg, `'${item.value}'`) |
| | |
| | | viewlost: false, |
| | | lostmsg: '', |
| | | config: {}, |
| | | userConfig: null, |
| | | userParam: null, |
| | | searchlist: null, |
| | | actions: null, |
| | | columns: null, |
| | |
| | | * @description 表格中,按钮触发事件传递 |
| | | */ |
| | | buttonTrigger = (btn, record) => { |
| | | this.refs.mainButton.actionTrigger(btn, record) |
| | | this.setState({ |
| | | triggerBtn: { |
| | | uuid: new Date().getTime(), |
| | | parentId: this.props.MenuID, |
| | | button: btn, |
| | | data: record |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** |
| | |
| | | controlCustomSetting = () => { |
| | | this.setState({ |
| | | settingVisible: true, |
| | | confirmLoading: false |
| | | confirmLoading: false, |
| | | revertLoading: false |
| | | }) |
| | | } |
| | | |
| | | changeMenuParam = (param) => { |
| | | this.setState({userParam: param}) |
| | | } |
| | | |
| | | linkTrigger = (menu) => { |
| | | const { tabviews, MenuID } = this.props |
| | | |
| | | menu.selected = true |
| | | |
| | | let index = 0 |
| | | let isexit = false |
| | | let tabs = tabviews.map((tab, i) => { |
| | | tab.selected = false |
| | | |
| | | if (tab.MenuID === MenuID) { |
| | | index = i |
| | | } else if (tab.MenuID === menu.MenuID) { |
| | | tab.param = menu.param |
| | | tab.selected = true |
| | | isexit = true |
| | | } |
| | | |
| | | return tab |
| | | }) |
| | | |
| | | if (!isexit) { |
| | | tabs.splice(index + 1, 0, menu) |
| | | } |
| | | |
| | | this.props.modifyTabview(tabs) |
| | | } |
| | | |
| | | settingRevert = () => { |
| | | let param = { |
| | | func: 's_TrdMenu_UserParam_del', |
| | | MenuID: this.props.MenuID |
| | | } |
| | | this.setState({ |
| | | revertLoading: true |
| | | }) |
| | | |
| | | Api.getSystemConfig(param).then(result => { |
| | | if (!result.status) { |
| | | this.setState({ |
| | | revertLoading: false |
| | | }) |
| | | notification.warning({ |
| | | top: 92, |
| | | message: result.message, |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | this.setState({ |
| | | settingVisible: false, |
| | | revertLoading: false |
| | | }, () => { |
| | | window.GLOB.CacheMap = new Map() |
| | | this.reloadview() |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | settingSubmit = () => { |
| | | this.verifyRef.handleConfirm().then(res => { |
| | | let _LongParam = '' |
| | | const { userParam } = this.state |
| | | let _LongParam = '' |
| | | |
| | | try { |
| | | _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(res))) |
| | | } catch (e) { |
| | | try { |
| | | _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(userParam))) |
| | | } catch (e) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '编译错误', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | let easyCode = userParam[this.props.MenuID] ? userParam[this.props.MenuID].easyCode : '' |
| | | |
| | | let param = { |
| | | func: 'sPC_TrdMenu_UserParam', |
| | | MenuID: this.props.MenuID, |
| | | EasyCode: easyCode || '', |
| | | LongParam: _LongParam |
| | | } |
| | | |
| | | this.setState({ |
| | | confirmLoading: true |
| | | }) |
| | | |
| | | Api.getSystemConfig(param).then(result => { |
| | | if (!result.status) { |
| | | this.setState({ |
| | | confirmLoading: false |
| | | }) |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '编译错误', |
| | | duration: 10 |
| | | message: result.message, |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | let param = { |
| | | func: 'sPC_TrdMenu_UserParam', |
| | | MenuID: this.props.MenuID, |
| | | LongParam: _LongParam |
| | | } |
| | | |
| | | this.setState({ |
| | | confirmLoading: true |
| | | }) |
| | | |
| | | Api.getSystemConfig(param).then(result => { |
| | | this.setState({ |
| | | settingVisible: false, |
| | | confirmLoading: false |
| | | }, () => { |
| | | window.GLOB.CacheMap = new Map() |
| | | this.reloadview() |
| | | }) |
| | | settingVisible: false, |
| | | confirmLoading: false |
| | | }, () => { |
| | | window.GLOB.CacheMap = new Map() |
| | | this.reloadview() |
| | | }) |
| | | }) |
| | | } |
| | |
| | | this.reloadview() |
| | | } |
| | | this.props.refreshTabView('') |
| | | } else if (!is(fromJS(this.props.tabviews), fromJS(nextProps.tabviews))) { |
| | | let selectTab = nextProps.tabviews.filter(tab => tab.selected)[0] |
| | | if (selectTab && selectTab.MenuID === this.props.MenuID) { |
| | | this.setShortcut() |
| | | } |
| | | } else if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) { |
| | | let search = this.state.search.map(item => { |
| | | if (item.type === 'text' && item.key === nextProps.param.searchkey) { |
| | | item.value = nextProps.param.searchval |
| | | } |
| | | |
| | | return item |
| | | }) |
| | | this.refreshbysearch(search) |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * @description 组件销毁,清除state更新 |
| | | * @description 组件销毁,清除state更新,清除快捷键设置 |
| | | */ |
| | | componentWillUnmount () { |
| | | this.setState = () => { |
| | | return |
| | | } |
| | | document.onkeydown = () => {} |
| | | } |
| | | |
| | | render() { |
| | | const { view, setting, searchlist, actions, columns, loadingview, viewlost, pickup, config } = this.state |
| | | const { view, setting, searchlist, actions, columns, loadingview, viewlost, pickup, config, triggerBtn, userConfig, tabActive, search } = this.state |
| | | |
| | | return ( |
| | | <div> |
| | |
| | | {actions && setting.onload !== 'false' ? |
| | | <div style={{minHeight: '25px'}}> |
| | | <MainAction |
| | | ref="mainButton" |
| | | BID="" |
| | | type="main" |
| | | menuType="main" |
| | | setting={setting} |
| | | actions={actions} |
| | | triggerBtn={triggerBtn} |
| | | dict={this.state.dict} |
| | | MenuID={this.props.MenuID} |
| | | permRoles={this.props.permRoles} |
| | |
| | | loading={this.state.loading} |
| | | refreshdata={this.refreshbytable} |
| | | buttonTrigger={this.buttonTrigger} |
| | | linkTrigger={this.linkTrigger} |
| | | handleTableId={this.handleTableId} |
| | | /> |
| | | </div> : null |
| | |
| | | if (config[group].length === 0) return null |
| | | |
| | | return ( |
| | | <Tabs defaultActiveKey="0" key={group}> |
| | | {config[group].map((_tab, index) => { |
| | | <Tabs activeKey={tabActive[group]} key={group} onChange={(key) => this.setState({tabActive: {...tabActive, [group]: key}})}> |
| | | {config[group].map(_tab => { |
| | | return ( |
| | | <TabPane tab={ |
| | | <span> |
| | | {_tab.icon ? <Icon type={_tab.icon} /> : null} |
| | | {_tab.label} |
| | | </span> |
| | | } key={`${index}`}> |
| | | } key={_tab.uuid}> |
| | | {_tab.type === 'SubTable' ? |
| | | <SubTable |
| | | Tab={_tab} |
| | | menuType="main" |
| | | MenuID={_tab.linkTab} |
| | | mainSearch={_tab.searchPass === 'true' ? search : null} |
| | | userConfig={userConfig ? userConfig[_tab.uuid] : null} |
| | | triggerBtn={triggerBtn} |
| | | SupMenuID={this.props.MenuID} |
| | | refreshtabs={this.state.refreshtabs} |
| | | ContainerId={this.state.ContainerId} |
| | |
| | | wrapClassName="common-table-custom-modal" |
| | | title={'自定义设置'} |
| | | maskClosable={false} |
| | | width={850} |
| | | width={950} |
| | | visible={this.state.settingVisible} |
| | | onOk={this.settingSubmit} |
| | | onCancel={() => { this.setState({ settingVisible: false }) }} |
| | | confirmLoading={this.state.confirmLoading} |
| | | footer={[ |
| | | <Button key="revert" type="danger" loading={this.state.revertLoading} onClick={this.settingRevert}>{this.state.dict['main.revert.default']}</Button>, |
| | | <Button key="cancel" onClick={() => { this.setState({ settingVisible: false }) }}>{this.state.dict['main.cancel']}</Button>, |
| | | <Button key="confirm" type="primary" loading={this.state.confirmLoading} onClick={this.settingSubmit}>{this.state.dict['main.submit']}</Button> |
| | | ]} |
| | | destroyOnClose |
| | | > |
| | | {this.state.config ? |
| | | {this.state.settingVisible ? |
| | | <VerifyCard |
| | | MenuID={this.props.MenuID} |
| | | MenuName={this.props.MenuName} |
| | | permAction={this.props.permAction} |
| | | permRoles={this.props.permRoles} |
| | | config={this.state.config} |
| | | wrappedComponentRef={(inst) => this.verifyRef = inst} |
| | | userConfig={this.state.userConfig} |
| | | columns={this.state.columns} |
| | | handleParam={this.changeMenuParam} |
| | | /> : null |
| | | } |
| | | </Modal> |
| | |
| | | tabviews: state.tabviews, |
| | | refreshTab: state.refreshTab, |
| | | permAction: state.permAction, |
| | | permMenus: state.permMenus, |
| | | permRoles: state.permRoles |
| | | } |
| | | } |