1
This commit is contained in:
@@ -18,6 +18,7 @@ export { default as modelFieldServer } from './modelFieldServer'
|
|||||||
export { default as modelServer } from './modelServer'
|
export { default as modelServer } from './modelServer'
|
||||||
export { default as paramSetupServer } from './paramSetupServer'
|
export { default as paramSetupServer } from './paramSetupServer'
|
||||||
export { default as sysControlTypeServer } from './sysControlTypeServer'
|
export { default as sysControlTypeServer } from './sysControlTypeServer'
|
||||||
|
export { default as sysTenantServer } from './sysTenantServer'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
22
src/api/system/sysTenantServer.js
Normal file
22
src/api/system/sysTenantServer.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
class SysTenantServer {
|
||||||
|
async list() {
|
||||||
|
return http.get('/sys_tenant/index', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(row) {
|
||||||
|
return http.post('/sys_tenant/add', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
async edit(row) {
|
||||||
|
return http.post('/sys_tenant/edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
async del(row) {
|
||||||
|
return http.post('/sys_tenant/del', row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sysTenantServer = new SysTenantServer()
|
||||||
|
export default sysTenantServer
|
||||||
@@ -186,20 +186,21 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 自适应表格宽度 */
|
/* 与 View Design 一致:横向/纵向滚动由 .ivu-table-body 上的 overflow 类控制,避免 wrapper 与整表同高导致横向条在页面最底 */
|
||||||
.ivu-table-wrapper {
|
.ivu-table-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-x: auto;
|
overflow: hidden;
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ivu-table {
|
.ivu-table {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 移除 table-layout: auto,使用 iView 默认的 fixed 布局以确保列对齐 */
|
/* 移除 table-layout: auto,使用 iView 默认的 fixed 布局以确保列对齐 */
|
||||||
@@ -208,8 +209,8 @@
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
.ivu-table-body {
|
.ivu-table-body {
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮风格一致 */
|
/* 按钮风格一致 */
|
||||||
|
|||||||
@@ -14,6 +14,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</Input>
|
</Input>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem prop="tenantCode">
|
||||||
|
<Input v-model="form.tenantCode" placeholder="租户编码,默认 default">
|
||||||
|
<span slot="prepend">
|
||||||
|
<Icon :size="14" type="ios-briefcase"></Icon>
|
||||||
|
</span>
|
||||||
|
</Input>
|
||||||
|
</FormItem>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Button style="margin-top:20px;" size="large" @click="handleSubmit" type="primary" long>登录</Button>
|
<Button style="margin-top:20px;" size="large" @click="handleSubmit" type="primary" long>登录</Button>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -40,7 +47,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
userName: '',
|
userName: '',
|
||||||
password: ''
|
password: '',
|
||||||
|
tenantCode: 'default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -48,7 +56,8 @@ export default {
|
|||||||
rules() {
|
rules() {
|
||||||
return {
|
return {
|
||||||
userName: this.userNameRules,
|
userName: this.userNameRules,
|
||||||
password: this.passwordRules
|
password: this.passwordRules,
|
||||||
|
tenantCode: [{ required: false }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -58,7 +67,8 @@ export default {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
this.$emit('on-success-valid', {
|
this.$emit('on-success-valid', {
|
||||||
userName: this.form.userName,
|
userName: this.form.userName,
|
||||||
password: this.form.password
|
password: this.form.password,
|
||||||
|
tenantCode: (this.form.tenantCode || '').trim() || 'default'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
<Header class="header-con-main">
|
<Header class="header-con-main">
|
||||||
<headerBar class="header-con" @on-coll-change="collpasedChange" :collapsed="collapsed">
|
<headerBar class="header-con" @on-coll-change="collpasedChange" :collapsed="collapsed">
|
||||||
<Terminal></Terminal>
|
<Terminal></Terminal>
|
||||||
|
<span
|
||||||
|
v-if="currentTenant"
|
||||||
|
class="main-tenant-tag"
|
||||||
|
:title="'租户:' + currentTenant.name + '(' + currentTenant.code + ')'"
|
||||||
|
>{{ currentTenant.name }}</span>
|
||||||
<user :userName="userName" :user-avator="userAvator || ''" />
|
<user :userName="userName" :user-avator="userAvator || ''" />
|
||||||
</headerBar>
|
</headerBar>
|
||||||
</Header>
|
</Header>
|
||||||
@@ -42,7 +47,7 @@ import User from './components/user'
|
|||||||
import ABackTop from './components/a-back-top'
|
import ABackTop from './components/a-back-top'
|
||||||
import Fullscreen from './components/fullscreen'
|
import Fullscreen from './components/fullscreen'
|
||||||
import Language from './components/language'
|
import Language from './components/language'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import headerBar from './components/header-bar'
|
import headerBar from './components/header-bar'
|
||||||
import loadFlower from '../load-flower/index'
|
import loadFlower from '../load-flower/index'
|
||||||
import Terminal from './components/terminal'
|
import Terminal from './components/terminal'
|
||||||
@@ -77,6 +82,7 @@ export default {
|
|||||||
userName: 'user/userName',
|
userName: 'user/userName',
|
||||||
userAvator: 'user/avatorImgPath'
|
userAvator: 'user/avatorImgPath'
|
||||||
}),
|
}),
|
||||||
|
...mapState('user', ['currentTenant']),
|
||||||
cacheList() {
|
cacheList() {
|
||||||
return ['ParentView']
|
return ['ParentView']
|
||||||
}
|
}
|
||||||
@@ -149,6 +155,21 @@ export default {
|
|||||||
z-index: 999999;
|
z-index: 999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-tenant-tag {
|
||||||
|
margin-right: 16px;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2d8cf0;
|
||||||
|
background: rgba(45, 140, 240, 0.08);
|
||||||
|
border-radius: 4px;
|
||||||
|
max-width: 160px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-brand {
|
.sidebar-brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -13,12 +13,25 @@
|
|||||||
<a v-if="isDown&&value&&value.length>0" class="link right-link" @click="downExecl">下载</a>
|
<a v-if="isDown&&value&&value.length>0" class="link right-link" @click="downExecl">下载</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table :class="tbClass" ref="tablesMain" border :data="insideTableData" :width='defaultWidth' :height='defaultHeight' :columns="insideColumns" v-bind="$attrs" @on-selection-change='onSelect'>
|
<div class="table-main">
|
||||||
|
<Table
|
||||||
|
v-bind="$attrs"
|
||||||
|
:class="tbClass"
|
||||||
|
ref="tablesMain"
|
||||||
|
border
|
||||||
|
:data="insideTableData"
|
||||||
|
:width="defaultWidth"
|
||||||
|
:height="defaultHeight"
|
||||||
|
:max-height="effectiveTableMaxHeight"
|
||||||
|
:columns="insideColumns"
|
||||||
|
@on-selection-change="onSelect"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<slot name="footer" slot="footer"></slot>
|
<slot name="footer" slot="footer"></slot>
|
||||||
<slot name="loading" slot="loading"></slot>
|
<slot name="loading" slot="loading"></slot>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -81,6 +94,9 @@ export default {
|
|||||||
},
|
},
|
||||||
// 动态计算的偏移量
|
// 动态计算的偏移量
|
||||||
calculatedOffset: null,
|
calculatedOffset: null,
|
||||||
|
/** 传给 iView Table 的 max-height(px),使表体在可视区内滚动,横向条始终在视口底部 */
|
||||||
|
bodyViewportMaxHeight: null,
|
||||||
|
_tableContentResizeObserver: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -145,6 +161,13 @@ export default {
|
|||||||
// 使用手动指定的偏移量
|
// 使用手动指定的偏移量
|
||||||
return `calc(100vh - ${this.maxHeightOffset}px)`
|
return `calc(100vh - ${this.maxHeightOffset}px)`
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 未传 height 时由容器测量得到,避免宽表横向滚动条落在整表最底部 */
|
||||||
|
effectiveTableMaxHeight() {
|
||||||
|
if (this.height) return undefined
|
||||||
|
if (this.bodyViewportMaxHeight == null) return undefined
|
||||||
|
return this.bodyViewportMaxHeight
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -174,6 +197,7 @@ export default {
|
|||||||
// 触发表格重新渲染
|
// 触发表格重新渲染
|
||||||
this.$refs.tablesMain.$forceUpdate()
|
this.$refs.tablesMain.$forceUpdate()
|
||||||
}
|
}
|
||||||
|
this.syncTableBodyMaxHeight()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onChangePage(page) {
|
onChangePage(page) {
|
||||||
@@ -184,6 +208,43 @@ export default {
|
|||||||
this.$emit('on-select', selection, row)
|
this.$emit('on-select', selection, row)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
syncTableBodyMaxHeight() {
|
||||||
|
if (this.height) {
|
||||||
|
this.bodyViewportMaxHeight = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const wrap = this.$refs.table_content
|
||||||
|
if (!wrap) return
|
||||||
|
const titleEl = wrap.querySelector('.table-title')
|
||||||
|
const titleH = (this.title || this.isDown) && titleEl ? titleEl.offsetHeight : 0
|
||||||
|
const avail = wrap.clientHeight - titleH
|
||||||
|
const next = Math.max(160, Math.floor(avail))
|
||||||
|
if (this.bodyViewportMaxHeight !== next) {
|
||||||
|
this.bodyViewportMaxHeight = next
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const inst = this.$refs.tablesMain
|
||||||
|
if (inst && typeof inst.handleResize === 'function') {
|
||||||
|
inst.handleResize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setupTableContentResizeObserver() {
|
||||||
|
const el = this.$refs.table_content
|
||||||
|
if (!el || typeof ResizeObserver === 'undefined') return
|
||||||
|
if (this._tableContentResizeObserver) {
|
||||||
|
this._tableContentResizeObserver.disconnect()
|
||||||
|
}
|
||||||
|
const ro = new ResizeObserver(() => {
|
||||||
|
this.syncTableBodyMaxHeight()
|
||||||
|
})
|
||||||
|
ro.observe(el)
|
||||||
|
this._tableContentResizeObserver = ro
|
||||||
|
},
|
||||||
|
|
||||||
// 动态计算表格容器上方所有元素的高度总和
|
// 动态计算表格容器上方所有元素的高度总和
|
||||||
calculateOffset() {
|
calculateOffset() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -203,6 +264,9 @@ export default {
|
|||||||
|
|
||||||
// 计算总偏移量
|
// 计算总偏移量
|
||||||
this.calculatedOffset = topOffset + pageBoxHeight + bottomPadding
|
this.calculatedOffset = topOffset + pageBoxHeight + bottomPadding
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncTableBodyMaxHeight()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -243,6 +307,7 @@ export default {
|
|||||||
|
|
||||||
// 动态计算偏移量
|
// 动态计算偏移量
|
||||||
this.calculateOffset()
|
this.calculateOffset()
|
||||||
|
this.setupTableContentResizeObserver()
|
||||||
|
|
||||||
// 如果数据或列配置为空,输出警告
|
// 如果数据或列配置为空,输出警告
|
||||||
if (!this.insideTableData || this.insideTableData.length === 0) {
|
if (!this.insideTableData || this.insideTableData.length === 0) {
|
||||||
@@ -255,11 +320,17 @@ export default {
|
|||||||
|
|
||||||
// 监听窗口大小变化,重新计算偏移量
|
// 监听窗口大小变化,重新计算偏移量
|
||||||
window.addEventListener('resize', this.calculateOffset)
|
window.addEventListener('resize', this.calculateOffset)
|
||||||
|
window.addEventListener('resize', this.syncTableBodyMaxHeight)
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
// 移除窗口大小监听
|
// 移除窗口大小监听
|
||||||
window.removeEventListener('resize', this.calculateOffset)
|
window.removeEventListener('resize', this.calculateOffset)
|
||||||
|
window.removeEventListener('resize', this.syncTableBodyMaxHeight)
|
||||||
|
if (this._tableContentResizeObserver) {
|
||||||
|
this._tableContentResizeObserver.disconnect()
|
||||||
|
this._tableContentResizeObserver = null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -268,8 +339,12 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.table-content {
|
.table-content {
|
||||||
overflow-y: auto;
|
/* 纵向由 Table 内部表体滚动;避免与外层双滚动导致横向条在整页最底 */
|
||||||
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.table-title {
|
.table-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -279,10 +354,17 @@ export default {
|
|||||||
padding: 0rem 0.15rem;
|
padding: 0rem 0.15rem;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
|
flex-shrink: 0;
|
||||||
.right-link {
|
.right-link {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-main {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-box {
|
.page-box {
|
||||||
|
|||||||
@@ -115,6 +115,17 @@ export const defaultMenus = [
|
|||||||
is_show_menu: 1,
|
is_show_menu: 1,
|
||||||
icon: 'md-text',
|
icon: 'md-text',
|
||||||
sort: 3
|
sort: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 125,
|
||||||
|
name: '租户管理',
|
||||||
|
path: '/system/tenant',
|
||||||
|
component: 'system/sys_tenant',
|
||||||
|
parent_id: 120,
|
||||||
|
type: '页面',
|
||||||
|
is_show_menu: 1,
|
||||||
|
icon: 'md-git-branch',
|
||||||
|
sort: 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,16 @@ import uiTool from '../utils/uiTool'
|
|||||||
import { defaultMenus, filterMenusByIds } from '../config/menuConfig'
|
import { defaultMenus, filterMenusByIds } from '../config/menuConfig'
|
||||||
import userServer from '../api/system/userServer'
|
import userServer from '../api/system/userServer'
|
||||||
|
|
||||||
|
function readTenantLs() {
|
||||||
|
try {
|
||||||
|
const s = localStorage.getItem('currentTenant')
|
||||||
|
if (!s || s === 'undefined') return null
|
||||||
|
return JSON.parse(s)
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
@@ -10,7 +20,9 @@ export default {
|
|||||||
avatorImgPath: '',
|
avatorImgPath: '',
|
||||||
token: getToken(),
|
token: getToken(),
|
||||||
authorityMenus: [],
|
authorityMenus: [],
|
||||||
menuList: localStorage.getItem('menuList') ? JSON.parse(localStorage.getItem('menuList')) : []
|
menuList: localStorage.getItem('menuList') ? JSON.parse(localStorage.getItem('menuList')) : [],
|
||||||
|
/** 登录返回的租户信息:{ id, name, code, is_platform } */
|
||||||
|
currentTenant: readTenantLs()
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setAvator(state, avatorPath) {
|
setAvator(state, avatorPath) {
|
||||||
@@ -31,6 +43,14 @@ export default {
|
|||||||
setMenuList(state, menus) {
|
setMenuList(state, menus) {
|
||||||
state.menuList = menus
|
state.menuList = menus
|
||||||
localStorage.setItem('menuList', JSON.stringify(menus))
|
localStorage.setItem('menuList', JSON.stringify(menus))
|
||||||
|
},
|
||||||
|
setCurrentTenant(state, tenant) {
|
||||||
|
state.currentTenant = tenant && typeof tenant === 'object' ? tenant : null
|
||||||
|
if (state.currentTenant) {
|
||||||
|
localStorage.setItem('currentTenant', JSON.stringify(state.currentTenant))
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('currentTenant')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@@ -162,6 +182,11 @@ export default {
|
|||||||
|
|
||||||
commit('setUserName', name)
|
commit('setUserName', name)
|
||||||
commit('setToken', token)
|
commit('setToken', token)
|
||||||
|
if (res.data.tenant) {
|
||||||
|
commit('setCurrentTenant', res.data.tenant)
|
||||||
|
} else {
|
||||||
|
commit('setCurrentTenant', null)
|
||||||
|
}
|
||||||
|
|
||||||
// 登录接口返回的 authorityMenus 是菜单 ID 数组字符串
|
// 登录接口返回的 authorityMenus 是菜单 ID 数组字符串
|
||||||
console.log('登录返回的菜单 IDs:', authorityMenusIds)
|
console.log('登录返回的菜单 IDs:', authorityMenusIds)
|
||||||
@@ -205,6 +230,7 @@ export default {
|
|||||||
commit('setToken', '')
|
commit('setToken', '')
|
||||||
commit('setAuthorityMenus', '[]')
|
commit('setAuthorityMenus', '[]')
|
||||||
commit('setMenuList', [])
|
commit('setMenuList', [])
|
||||||
|
commit('setCurrentTenant', null)
|
||||||
localStorage.removeItem('menuList')
|
localStorage.removeItem('menuList')
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import SysLog from './system/sys_log.vue'
|
|||||||
import SysParamSetup from './system/sys_param_setup.vue'
|
import SysParamSetup from './system/sys_param_setup.vue'
|
||||||
import SysRole from './system/sys_role.vue'
|
import SysRole from './system/sys_role.vue'
|
||||||
import SysUser from './system/sys_user.vue'
|
import SysUser from './system/sys_user.vue'
|
||||||
|
import SysTenant from './system/sys_tenant.vue'
|
||||||
import SysLogOperate from './system/sys_log_operate.vue'
|
import SysLogOperate from './system/sys_log_operate.vue'
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ export function setupComponentMap(customMap = {}, uiTool) {
|
|||||||
'system/sys_param_setup': SysParamSetup,
|
'system/sys_param_setup': SysParamSetup,
|
||||||
'system/sys_role': SysRole,
|
'system/sys_role': SysRole,
|
||||||
'system/sys_user': SysUser,
|
'system/sys_user': SysUser,
|
||||||
|
'system/sys_tenant': SysTenant,
|
||||||
'system/sys_control': SysControl,
|
'system/sys_control': SysControl,
|
||||||
'system/sys_menu': SysMenu,
|
'system/sys_menu': SysMenu,
|
||||||
'system/sys_title': SysTitle,
|
'system/sys_title': SysTitle,
|
||||||
@@ -60,6 +62,7 @@ export default {
|
|||||||
SysParamSetup,
|
SysParamSetup,
|
||||||
SysRole,
|
SysRole,
|
||||||
SysUser,
|
SysUser,
|
||||||
|
SysTenant,
|
||||||
SysControl,
|
SysControl,
|
||||||
SysMenu,
|
SysMenu,
|
||||||
SysTitle,
|
SysTitle,
|
||||||
|
|||||||
@@ -43,9 +43,13 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('user', ['handleLogin']),
|
...mapActions('user', ['handleLogin']),
|
||||||
async handleSubmit({ userName, password }) {
|
async handleSubmit({ userName, password, tenantCode }) {
|
||||||
try {
|
try {
|
||||||
let userFrom = { name: userName, password: password }
|
let userFrom = {
|
||||||
|
name: userName,
|
||||||
|
password: password,
|
||||||
|
tenant_code: tenantCode || 'default'
|
||||||
|
}
|
||||||
await this.handleLogin({
|
await this.handleLogin({
|
||||||
userFrom,
|
userFrom,
|
||||||
Main,
|
Main,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content-view">
|
<div class="content-view">
|
||||||
|
<Alert type="info" show-icon closable style="margin-bottom: 12px">
|
||||||
|
角色为<strong>全库共用</strong>,不按租户隔离;各租户用户可在「用户管理」中绑定同一角色,菜单权限以角色配置为准。
|
||||||
|
</Alert>
|
||||||
<div class="table-head-tool">
|
<div class="table-head-tool">
|
||||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,6 +76,23 @@ export default {
|
|||||||
this.init()
|
this.init()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
parseRoleMenuIds(menus) {
|
||||||
|
if (menus == null || menus === '') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (Array.isArray(menus)) {
|
||||||
|
return menus.map((id) => Number(id)).filter((id) => !Number.isNaN(id))
|
||||||
|
}
|
||||||
|
if (typeof menus === 'string') {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(menus)
|
||||||
|
return this.parseRoleMenuIds(parsed)
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
async init() {
|
async init() {
|
||||||
let res = await roleServer.list()
|
let res = await roleServer.list()
|
||||||
this.gridOption.data = res.data
|
this.gridOption.data = res.data
|
||||||
@@ -120,12 +140,8 @@ export default {
|
|||||||
|
|
||||||
let res = await menuServer.list()
|
let res = await menuServer.list()
|
||||||
let tree = uiTool.transformTree(res.data)
|
let tree = uiTool.transformTree(res.data)
|
||||||
if (row.menus) {
|
this.expandTreeAll = this.parseRoleMenuIds(row.menus)
|
||||||
this.expandTreeAll = JSON.parse(row.menus)
|
|
||||||
this.treeData = this.mapTree(tree)
|
this.treeData = this.mapTree(tree)
|
||||||
} else {
|
|
||||||
this.treeData = this.mapTree(tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isShowPermission = true
|
this.isShowPermission = true
|
||||||
},
|
},
|
||||||
@@ -136,7 +152,7 @@ export default {
|
|||||||
p.children = this.mapTree(p.children)
|
p.children = this.mapTree(p.children)
|
||||||
}
|
}
|
||||||
|
|
||||||
let row = this.expandTreeAll.find((p2) => p2 === p.id)
|
let row = this.expandTreeAll.find((p2) => Number(p2) === Number(p.id))
|
||||||
if (row) {
|
if (row) {
|
||||||
p.checked = true
|
p.checked = true
|
||||||
}
|
}
|
||||||
|
|||||||
127
src/views/system/sys_tenant.vue
Normal file
127
src/views/system/sys_tenant.vue
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-view">
|
||||||
|
<Alert type="warning" show-icon style="margin-bottom: 12px">
|
||||||
|
仅<strong>平台租户</strong>(is_platform=1)可维护租户列表;普通租户登录后本页仅能看到自身租户信息。
|
||||||
|
需在数据库执行迁移脚本创建 <code>sys_tenant</code> 表及默认数据。
|
||||||
|
</Alert>
|
||||||
|
<div class="table-head-tool">
|
||||||
|
<Button type="primary" @click="showAddWarp">新增租户</Button>
|
||||||
|
</div>
|
||||||
|
<div class="table-body">
|
||||||
|
<tables ref="tables" v-model="gridOption.data" :columns="gridOption.columns" />
|
||||||
|
</div>
|
||||||
|
<editModal ref="editModal" :columns="gridOption.columns" :data="gridOption.editRow" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import uiTool from '@/utils/uiTool'
|
||||||
|
import sysTenantServer from '@/api/system/sysTenantServer'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'sys_tenant_page',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
gridOption: {
|
||||||
|
editRow: {},
|
||||||
|
columns: [
|
||||||
|
{ title: 'id', key: 'id' },
|
||||||
|
{ title: '名称', key: 'name' },
|
||||||
|
{ title: '编码', key: 'code' },
|
||||||
|
{ title: '备注', key: 'remark' },
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
com: 'Radio',
|
||||||
|
source: [
|
||||||
|
{ key: 1, value: '启用' },
|
||||||
|
{ key: 0, value: '停用' }
|
||||||
|
],
|
||||||
|
render(h, p) {
|
||||||
|
return h('span', p.row.status === 1 ? '启用' : '停用')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '平台租户',
|
||||||
|
key: 'is_platform',
|
||||||
|
render(h, p) {
|
||||||
|
return h('span', Number(p.row.is_platform) === 1 ? '是' : '否')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
render: (h, params) => {
|
||||||
|
if (params.row.id === 1) {
|
||||||
|
return h('span', { style: { color: '#999' } }, '内置租户')
|
||||||
|
}
|
||||||
|
const btns = [
|
||||||
|
{
|
||||||
|
title: '修改',
|
||||||
|
type: 'primary',
|
||||||
|
click: () => this.showEditWarp(params.row)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '删除',
|
||||||
|
type: 'primary',
|
||||||
|
click: () => this.delConfirm(params.row)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return uiTool.getBtn(h, btns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async init() {
|
||||||
|
const res = await sysTenantServer.list()
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
this.gridOption.data = res.data || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showAddWarp() {
|
||||||
|
this.$refs.editModal.addShow(
|
||||||
|
{ status: 1 },
|
||||||
|
async (row) => {
|
||||||
|
await sysTenantServer.add(row)
|
||||||
|
this.$Message.success('新增成功')
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
showEditWarp(row) {
|
||||||
|
this.$refs.editModal.editShow(row, async (newRow) => {
|
||||||
|
await sysTenantServer.edit(newRow)
|
||||||
|
this.$Message.success('修改成功')
|
||||||
|
this.init()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
delConfirm(row) {
|
||||||
|
uiTool.delConfirm(async () => {
|
||||||
|
await sysTenantServer.del(row)
|
||||||
|
this.$Message.success('删除成功')
|
||||||
|
this.init()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.table-body {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content-view">
|
<div class="content-view">
|
||||||
|
<Alert v-if="currentTenant" type="info" show-icon closable style="margin-bottom: 12px">
|
||||||
|
当前租户:<strong>{{ currentTenant.name }}</strong>({{ currentTenant.code }})。用户按租户隔离;
|
||||||
|
<strong>角色全库共用</strong>,下拉中的角色对所有租户一致。
|
||||||
|
<span v-if="isPlatformTenant">平台租户可为他人指定「目标租户」。</span>
|
||||||
|
</Alert>
|
||||||
|
|
||||||
<div class="table-head-tool">
|
<div class="table-head-tool">
|
||||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||||
@@ -16,9 +21,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import userServer from '@/api/system/userServer'
|
import userServer from '@/api/system/userServer'
|
||||||
import roleServer from '@/api/system/roleServer'
|
import roleServer from '@/api/system/roleServer'
|
||||||
|
import sysTenantServer from '@/api/system/sysTenantServer'
|
||||||
import uiTool from '@/utils/uiTool'
|
import uiTool from '@/utils/uiTool'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -30,7 +36,17 @@ export default {
|
|||||||
editRow: {},
|
editRow: {},
|
||||||
columns: [
|
columns: [
|
||||||
{ title: '登陆名', key: 'name' },
|
{ title: '登陆名', key: 'name' },
|
||||||
|
{
|
||||||
|
title: '租户',
|
||||||
|
key: 'tenantName',
|
||||||
|
display: true,
|
||||||
|
render(h, params) {
|
||||||
|
if (params.row.tenant) {
|
||||||
|
return h('span', params.row.tenant.name || '')
|
||||||
|
}
|
||||||
|
return h('span', '')
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '密码',
|
title: '密码',
|
||||||
key: 'password',
|
key: 'password',
|
||||||
@@ -83,19 +99,37 @@ export default {
|
|||||||
...mapGetters({
|
...mapGetters({
|
||||||
shopList: 'shop/shopList',
|
shopList: 'shop/shopList',
|
||||||
}),
|
}),
|
||||||
|
...mapState('user', ['currentTenant']),
|
||||||
|
isPlatformTenant() {
|
||||||
|
return this.currentTenant && Number(this.currentTenant.is_platform) === 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentTenant() {
|
||||||
|
this.syncTenantSelectColumn()
|
||||||
|
this.initCol()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.init()
|
this.init()
|
||||||
this.initCol()
|
this.initCol()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
syncTenantSelectColumn() {
|
||||||
|
const idx = this.gridOption.columns.findIndex((c) => c.key === 'tenant_id_select')
|
||||||
|
if (idx !== -1) {
|
||||||
|
this.gridOption.columns.splice(idx, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
async init() {
|
async init() {
|
||||||
let res = await userServer.all()
|
let res = await userServer.all()
|
||||||
this.gridOption.data = res.data
|
this.gridOption.data = res.data
|
||||||
},
|
},
|
||||||
async initCol() {
|
async initCol() {
|
||||||
|
this.syncTenantSelectColumn()
|
||||||
|
|
||||||
let res = await roleServer.list()
|
let res = await roleServer.list()
|
||||||
this.roles = res.data
|
this.roles = res.data || []
|
||||||
let roleSource = this.roles.map((p) => {
|
let roleSource = this.roles.map((p) => {
|
||||||
return {
|
return {
|
||||||
key: p.id,
|
key: p.id,
|
||||||
@@ -107,18 +141,51 @@ export default {
|
|||||||
if (roleRow) {
|
if (roleRow) {
|
||||||
roleRow.source = roleSource
|
roleRow.source = roleSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isPlatformTenant) {
|
||||||
|
try {
|
||||||
|
const tr = await sysTenantServer.list()
|
||||||
|
const tenants = (tr && tr.data) || []
|
||||||
|
const tenantSource = tenants.map((t) => ({
|
||||||
|
key: t.id,
|
||||||
|
value: `${t.name}(${t.code})`,
|
||||||
|
}))
|
||||||
|
this.gridOption.columns.splice(1, 0, {
|
||||||
|
title: '目标租户',
|
||||||
|
key: 'tenant_id_select',
|
||||||
|
com: 'Select',
|
||||||
|
source: tenantSource,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('加载租户列表失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
showAddWarp() {
|
showAddWarp() {
|
||||||
this.$refs.editModal.addShow({}, async (newRow) => {
|
this.$refs.editModal.addShow({}, async (newRow) => {
|
||||||
await userServer.add(newRow)
|
const payload = { ...newRow }
|
||||||
|
if (payload.tenant_id_select != null && payload.tenant_id_select !== '') {
|
||||||
|
payload.tenant_id = Number(payload.tenant_id_select)
|
||||||
|
}
|
||||||
|
delete payload.tenant_id_select
|
||||||
|
await userServer.add(payload)
|
||||||
this.$Message.success('新增成功!')
|
this.$Message.success('新增成功!')
|
||||||
this.init()
|
this.init()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
showEditWarp(row) {
|
showEditWarp(row) {
|
||||||
this.$refs.editModal.editShow(row, async (newRow) => {
|
const rowWithSelect = { ...row }
|
||||||
await userServer.edit(newRow)
|
if (this.isPlatformTenant) {
|
||||||
|
rowWithSelect.tenant_id_select = row.tenant_id
|
||||||
|
}
|
||||||
|
this.$refs.editModal.editShow(rowWithSelect, async (newRow) => {
|
||||||
|
const payload = { ...newRow }
|
||||||
|
if (payload.tenant_id_select != null && payload.tenant_id_select !== '') {
|
||||||
|
payload.tenant_id = Number(payload.tenant_id_select)
|
||||||
|
}
|
||||||
|
delete payload.tenant_id_select
|
||||||
|
await userServer.edit(payload)
|
||||||
this.$Message.success('修改成功!')
|
this.$Message.success('修改成功!')
|
||||||
this.init()
|
this.init()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user