提交 4ba741f8 authored 作者: 王鹏飞's avatar 王鹏飞

v2

上级 87dc63ee
...@@ -5,6 +5,7 @@ module.exports = { ...@@ -5,6 +5,7 @@ module.exports = {
extends: ['plugin:vue/essential', 'standard'], extends: ['plugin:vue/essential', 'standard'],
rules: { rules: {
'vue/comment-directive': 'off', 'vue/comment-directive': 'off',
'vue/multi-word-component-names': 'off',
'space-before-function-paren': 'off' 'space-before-function-paren': 'off'
} }
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
{ {
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"dev": "vite --mode dev", "dev": "vite",
"build": "cross-env BUILD_ENV=prod vite build && cross-env BUILD_ENV=prod npm run deploy", "build": "vite build && npm run deploy",
"build:pre": "vite build", "build:pre": "vite build --mode pre",
"build:test": "vite build --mode test", "build:test": "vite build --mode test",
"preview": "vite preview", "preview": "vite preview",
"deploy": "node ./deploy.js", "deploy": "node ./deploy.js",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src" "lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
}, },
"dependencies": { "dependencies": {
"axios": "^0.24.0", "axios": "^0.26.0",
"element-ui": "^2.15.6", "element-ui": "^2.15.6",
"lodash": "^4.17.21",
"query-string": "^7.0.1", "query-string": "^7.0.1",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-router": "^3.5.3", "vue-router": "^3.5.3",
"vuex": "^3.6.2" "vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-eslint": "^8.0.1", "ali-oss": "^6.17.1",
"ali-oss": "^6.16.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"cross-env": "^7.0.3",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3", "eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.2", "eslint-plugin-import": "^2.25.2",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0", "eslint-plugin-promise": "^5.2.0",
"eslint-plugin-vue": "^7.19.1", "eslint-plugin-vue": "^8.4.1",
"sass": "1.42.1", "sass": "1.49.7",
"vite": "^2.7.3", "vite": "^2.8.3",
"vite-plugin-vue2": "^1.9.0", "vite-plugin-checker": "^0.4.2",
"vite-plugin-vue2": "^1.9.3",
"vue-template-compiler": "^2.6.14" "vue-template-compiler": "^2.6.14"
} }
} }
...@@ -34,3 +34,10 @@ export function uploadFile(data) { ...@@ -34,3 +34,10 @@ export function uploadFile(data) {
export function getPermissions(params) { export function getPermissions(params) {
return httpRequest.get('/api/permissions/api/v1/user/permissions', { params }) return httpRequest.get('/api/permissions/api/v1/user/permissions', { params })
} }
/**
* 获取所有项目列表
*/
export function getAllProjects(params) {
return httpRequest.get('/api/xedu/admin/v2/project/all', { params })
}
...@@ -46,6 +46,12 @@ export default { ...@@ -46,6 +46,12 @@ export default {
name: '权限管理', name: '权限管理',
path: '/permissions', path: '/permissions',
icon: 'el-icon-coordinate' icon: 'el-icon-coordinate'
},
{
tag: 'menu_project',
name: '项目管理',
path: '/projects',
icon: 'el-icon-folder-opened'
} }
] ]
} }
...@@ -83,17 +89,17 @@ export default { ...@@ -83,17 +89,17 @@ export default {
<style lang="scss"> <style lang="scss">
.app-aside { .app-aside {
position: sticky;
top: 0;
width: 200px; width: 200px;
z-index: 100;
background: #fff; background: #fff;
border-right: 1px solid rgba(0, 0, 0, 0.12); border-right: 1px solid rgba(0, 0, 0, 0.12);
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
flex: 0 0 200px; flex: 0 0 200px;
box-sizing: content-box;
} }
.nav { .nav {
// position: fixed;
width: 200px;
margin: 20px 0; margin: 20px 0;
.el-menu { .el-menu {
border-right: 0; border-right: 0;
......
...@@ -4,14 +4,25 @@ ...@@ -4,14 +4,25 @@
<router-link to="/"><img src="https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo-white.svg" /></router-link> <router-link to="/"><img src="https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo-white.svg" /></router-link>
</div> </div>
<div class="app-header-right"> <div class="app-header-right">
<el-select :value="activeProject.id" placeholder="所属项目" size="medium" clearable @change="handleChangeProject">
<el-option v-for="item in projects" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-dropdown> <el-dropdown>
<div class="avatar"> <div class="avatar">
<img :src="user.avatar || 'https://zws-imgs-pub.ezijing.com/pc/base/logo.png'" /> <img :src="user.avatar || 'https://webapp-pub.ezijing.com/website/base/images/avatar.svg'" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown" style="width: 280px">
<div class="user"> <div class="app-header-user">
<div class="user-name">{{ user.realname || user.nickname }}</div> <div class="app-header-user-avatar">
<el-button round size="medium" class="btn-logout" @click="logout">退出登录</el-button> <img :src="user.avatar || 'https://webapp-pub.ezijing.com/website/base/images/avatar.svg'" />
</div>
<div class="app-header-user-main">
<h3>{{ user.realname || user.nickname }}</h3>
<p>{{ user.email || user.mobile }}</p>
</div>
<div class="app-header-user-buttons">
<el-button size="medium" round @click="logout">退出登录</el-button>
</div>
</div> </div>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
...@@ -24,7 +35,13 @@ export default { ...@@ -24,7 +35,13 @@ export default {
name: 'AppHeader', name: 'AppHeader',
computed: { computed: {
user() { user() {
return this.$store.state.user return this.$store.state.user || {}
},
projects() {
return this.$store.state.projects || []
},
activeProject() {
return this.$store.state.activeProject || {}
} }
}, },
methods: { methods: {
...@@ -32,6 +49,11 @@ export default { ...@@ -32,6 +49,11 @@ export default {
this.$store.dispatch('logout').then(() => { this.$store.dispatch('logout').then(() => {
window.location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(window.location.href)}` window.location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(window.location.href)}`
}) })
},
handleChangeProject(id) {
const project = this.projects.find(item => item.id === id) || {}
this.$store.commit('setActiveProjedct', project)
this.$router.push({ path: '/', query: { project_id: id } })
} }
} }
} }
...@@ -39,6 +61,9 @@ export default { ...@@ -39,6 +61,9 @@ export default {
<style lang="scss"> <style lang="scss">
.app-header { .app-header {
// position: sticky;
// top: 0;
// z-index: 1000;
padding: 0 20px; padding: 0 20px;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -53,12 +78,14 @@ export default { ...@@ -53,12 +78,14 @@ export default {
} }
.app-header-right { .app-header-right {
display: flex; display: flex;
align-items: center;
.avatar { .avatar {
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
margin-left: 20px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -66,15 +93,50 @@ export default { ...@@ -66,15 +93,50 @@ export default {
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
} }
&:hover {
background-color: rgba(60, 64, 67, 0.08);
}
} }
} }
.user { .app-header-user {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 16px; padding: 16px;
} }
.user-name { .app-header-user-avatar {
margin-bottom: 20px; margin-bottom: 6px;
width: 80px;
height: 80px;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.app-header-user-main {
h3 {
color: #202124;
font: 500 16px/22px Helvetica, Arial, sans-serif;
letter-spacing: 0.29px;
margin: 0;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
}
p {
color: #5f6368;
font: 400 14px/19px Helvetica, Arial, sans-serif;
letter-spacing: normal;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
}
} }
.btn-logout { .app-header-user-buttons {
width: 200px; padding-top: 16px;
} }
</style> </style>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="app-layout"> <div class="app-layout">
<app-header></app-header> <app-header></app-header>
<div class="app-layout-container"> <div class="app-layout-container">
<app-aside></app-aside> <app-aside v-if="sidebar"></app-aside>
<app-main></app-main> <app-main></app-main>
</div> </div>
</div> </div>
...@@ -15,6 +15,7 @@ import AppMain from './Main.vue' ...@@ -15,6 +15,7 @@ import AppMain from './Main.vue'
export default { export default {
name: 'AppLayout', name: 'AppLayout',
props: { sidebar: { type: Boolean, default: true } },
components: { AppHeader, AppAside, AppMain } components: { AppHeader, AppAside, AppMain }
} }
</script> </script>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<app-breadcrumb v-if="hasBreadcrumb"></app-breadcrumb> <app-breadcrumb v-if="hasBreadcrumb"></app-breadcrumb>
</div> </div>
<div class="app-main-container"> <div class="app-main-container">
<router-view></router-view> <router-view :key="$route.fullPath"></router-view>
</div> </div>
</div> </div>
</section> </section>
......
...@@ -39,8 +39,8 @@ export default { ...@@ -39,8 +39,8 @@ export default {
this.dispatch('ElFormItem', 'el.form.blur', editor.getContent()) this.dispatch('ElFormItem', 'el.form.blur', editor.getContent())
}, },
dispatch(componentName, eventName, params) { dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root let parent = this.$parent || this.$root
var name = parent.$options.componentName let name = parent.$options.componentName
while (parent && (!name || name !== componentName)) { while (parent && (!name || name !== componentName)) {
parent = parent.$parent parent = parent.$parent
......
...@@ -144,8 +144,8 @@ export default { ...@@ -144,8 +144,8 @@ export default {
this.$emit('input', this.fileList) this.$emit('input', this.fileList)
}, },
dispatch(componentName, eventName, params) { dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root let parent = this.$parent || this.$root
var name = parent.$options.componentName let name = parent.$options.componentName
while (parent && (!name || name !== componentName)) { while (parent && (!name || name !== componentName)) {
parent = parent.$parent parent = parent.$parent
......
...@@ -13,8 +13,6 @@ import './assets/theme/style.scss' ...@@ -13,8 +13,6 @@ import './assets/theme/style.scss'
import AppCard from './components/base/AppCard.vue' import AppCard from './components/base/AppCard.vue'
import AppList from './components/base/AppList.vue' import AppList from './components/base/AppList.vue'
import beforeEnter from './utils/beforeEnter'
// 注册element-ui组件 // 注册element-ui组件
Vue.use(ElementUI, { size: 'small' }) Vue.use(ElementUI, { size: 'small' })
...@@ -25,8 +23,6 @@ Vue.component('app-list', AppList) ...@@ -25,8 +23,6 @@ Vue.component('app-list', AppList)
// 注册模块 // 注册模块
modules({ router, store }) modules({ router, store })
router.beforeEach(beforeEnter)
new Vue({ new Vue({
store, store,
router, router,
......
...@@ -4,33 +4,33 @@ import httpRequest from '@/utils/axios' ...@@ -4,33 +4,33 @@ import httpRequest from '@/utils/axios'
* 获取权限列表 * 获取权限列表
*/ */
export function getPermissionList(params) { export function getPermissionList(params) {
return httpRequest.get('/api/xedu/admin/v1/permissions', { params }) return httpRequest.get('/api/xedu/admin/v2/permissions', { params })
} }
/** /**
* 获取权限详情 * 获取权限详情
*/ */
export function getPermission(params) { export function getPermission(params) {
return httpRequest.get(`/api/xedu/admin/v1/permission/${params.id}`, { params }) return httpRequest.get(`/api/xedu/admin/v2/permission/${params.id}`, { params })
} }
/** /**
* 创建权限 * 创建权限
*/ */
export function createPermission(data) { export function createPermission(data) {
return httpRequest.post('/api/xedu/admin/v1/permission', data) return httpRequest.post('/api/xedu/admin/v2/permission', data)
} }
/** /**
* 修改权限 * 修改权限
*/ */
export function updatePermission(data) { export function updatePermission(data) {
return httpRequest.put(`/api/xedu/admin/v1/permission/${data.id}`, data) return httpRequest.put(`/api/xedu/admin/v2/permission/${data.id}`, data)
} }
/** /**
* 删除权限 * 删除权限
*/ */
export function deletePermission(data) { export function deletePermission(data) {
return httpRequest.delete(`/api/xedu/admin/v1/permission/${data.id}`, data) return httpRequest.delete(`/api/xedu/admin/v2/permission/${data.id}`, data)
} }
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
<script> <script>
// lodash // lodash
import { createPermission, updatePermission } from '../api.js' import { createPermission, updatePermission } from '../api.js'
import { pick } from 'lodash'
export default { export default {
props: { props: {
isEdit: { type: Boolean, default: false }, isEdit: { type: Boolean, default: false },
...@@ -74,20 +75,21 @@ export default { ...@@ -74,20 +75,21 @@ export default {
// 确定 // 确定
onPrimary() { onPrimary() {
this.$refs.form.validate().then(() => { this.$refs.form.validate().then(() => {
this.isEdit ? this.edit() : this.create() const params = pick(this.form, ['id', 'parent_id', 'name', 'tag', 'type', 'system_tag', 'desc', 'effect_uris'])
this.isEdit ? this.edit(params) : this.create(params)
}) })
}, },
// 创建权限 // 创建权限
create() { create(params) {
createPermission(this.form).then(res => { createPermission(params).then(res => {
this.$message.success('创建成功') this.$message.success('创建成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
}) })
}, },
// 编辑权限 // 编辑权限
edit() { edit(params) {
updatePermission(this.form).then(res => { updatePermission(params).then(res => {
this.$message.success('修改成功') this.$message.success('修改成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
......
import httpRequest from '@/utils/axios'
/**
* 获取项目列表
*/
export function getProjectList(params) {
return httpRequest.get('/api/xedu/admin/v2/projects', { params })
}
/**
* 获取项目详情
*/
export function getProject(params) {
return httpRequest.get(`/api/xedu/admin/v2/${params.id}/project`, { params })
}
/**
* 创建项目
*/
export function createProject(data) {
return httpRequest.post('/api/xedu/admin/v2/project', data)
}
/**
* 修改项目
*/
export function updateProject(data) {
return httpRequest.put(`/api/xedu/admin/v2/${data.id}/project`, data)
}
/**
* 修改项目状态
*/
export function updateProjectStatus(data) {
return httpRequest.post(`/api/xedu/admin/v2/project/${data.id}/status`, data)
}
/**
* 删除项目
*/
export function deleteProject(data) {
return httpRequest.delete(`/api/xedu/admin/v2/${data.id}/project`, data)
}
<template>
<el-dialog :title="title" :close-on-click-modal="false" v-bind="$attrs" v-on="$listeners" width="500px">
<el-form ref="form" :model="form" :rules="rules" label-position="top">
<el-form-item label="项目名称" prop="name">
<el-input v-model="form.name" clearable placeholder="请输入项目名称" />
</el-form-item>
<el-form-item label="项目标识" prop="tag">
<el-input v-model="form.tag" clearable placeholder="请输入项目标识" :readonly="isEdit" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="text" @click="onCancel">取消</el-button>&nbsp;&nbsp;
<el-button type="primary" size="medium" @click="onPrimary">保存</el-button>
</template>
</el-dialog>
</template>
<script>
import { createProject, updateProject } from '../api.js'
import { pick } from 'lodash'
export default {
props: {
isEdit: { type: Boolean, default: false },
data: { type: Object, default: () => ({}) }
},
data() {
return {
form: { name: '', tag: '' },
rules: {
name: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
tag: [{ required: true, message: '请输入项目标识', trigger: 'blur' }]
}
}
},
watch: {
data: {
immediate: true,
handler(data) {
this.form = Object.assign({}, this.form, data)
}
}
},
computed: {
title() {
return this.isEdit ? '编辑项目' : '添加项目'
}
},
methods: {
// 取消
onCancel() {
this.$emit('update:visible', false)
},
// 确定
onPrimary() {
this.$refs.form.validate().then(() => {
const params = pick(this.form, ['id', 'name', 'tag'])
this.isEdit ? this.edit(params) : this.create(params)
})
},
// 添加项目
create(params) {
createProject(params).then(res => {
this.$message.success('添加成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
},
// 编辑项目
edit(params) {
updateProject(params).then(res => {
this.$message.success('编辑成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
}
}
}
</script>
<style></style>
const routes = [
{
path: '/projects',
component: () => import('@/components/layout/Index.vue'),
meta: { title: '项目管理' },
children: [{ path: '', component: () => import('./views/List.vue'), meta: { requireProjectId: false } }]
},
{
path: '/choose',
component: () => import('@/components/layout/Index.vue'),
props: { sidebar: false },
children: [{ path: '', component: () => import('./views/Choose.vue'), meta: { requireProjectId: false } }]
}
]
export { routes }
<template>
<div class="choose-list">
<el-card class="choose-item" v-for="item in projects" :key="item.id" @click.native="handleClick(item)">
<h2>{{ item.name }}</h2>
</el-card>
</div>
</template>
<script>
export default {
computed: {
projects() {
return this.$store.state.projects || []
}
},
methods: {
handleClick(item) {
this.$store.commit('setActiveProjedct', item)
this.$router.push({ path: '/', query: { project_id: item.id } })
}
}
}
</script>
<style lang="scss" scoped>
.choose-list {
display: flex;
flex-wrap: wrap;
}
.choose-item {
margin: 10px;
cursor: pointer;
}
</style>
<template>
<app-card>
<app-list v-bind="tableOptions" ref="list">
<template #header-aside>
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">添加项目</el-button>
</template>
<template v-slot:table-role="{ row }">
<el-tag>{{ row.role_name }}</el-tag>
</template>
<template v-slot:table-status="{ row }">
<el-switch
v-model="row.status"
:active-value="1"
:inactive-value="2"
active-text="启用"
inactive-text="停用"
@change="handleStatus(row)"
></el-switch>
</template>
<template v-slot:table-x="{ row }">
<el-button type="primary" plain @click="handleUpdate(row)">编辑</el-button>
</template>
</app-list>
<!-- 创建/编辑 -->
<editform
:visible.sync="visible"
:isEdit="isEdit"
:data="editRaw"
@success="handleSuccess"
v-if="visible"
></editform>
</app-card>
</template>
<script>
// 接口
import { getProjectList, deleteProject, updateProjectStatus } from '../api'
export default {
components: {
Editform: () => import('../components/Editform.vue')
},
data() {
return {
visible: false,
isEdit: false, // 是否是编辑状态
editRaw: {} // 编辑的数据
}
},
computed: {
// 列表配置
tableOptions() {
return {
remote: {
httpRequest: getProjectList,
params: { id: '', name: '', tag: '' }
},
filters: [
{
type: 'input',
prop: 'id',
placeholder: '项目ID'
},
{
type: 'input',
prop: 'name',
placeholder: '项目名称'
},
{
type: 'input',
prop: 'tag',
placeholder: '项目标识'
}
],
columns: [
{ label: '项目ID', prop: 'id' },
{ label: '项目名称', prop: 'name' },
{ label: '项目标识', prop: 'tag' },
{
label: '创建人',
prop: 'created_by.realname',
computed({ row }) {
return row.created_by.realname || row.created_by.username
}
},
{ label: '创建时间', prop: 'created_at' },
{ label: '状态', prop: 'status', slots: 'table-status' },
{ label: '操作', slots: 'table-x', align: 'right', width: '80', fixed: 'right' }
]
}
}
},
methods: {
// 创建成功刷新列表
handleSuccess() {
this.$refs.list.refetch()
},
// 创建
handleCreate() {
this.isEdit = false
this.editRaw = {}
this.visible = true
},
// 编辑
handleUpdate(row) {
this.isEdit = true
this.editRaw = row
this.visible = true
},
// 删除
onRemove(row) {
this.$confirm('项目删除请谨慎操作,确定删除?', '删除项目', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.handleRemove(row)
})
},
// 删除
handleRemove(row) {
deleteProject({ id: row.id }).then(res => {
this.$message({ type: 'success', message: '删除成功' })
this.$refs.list.refetch()
})
},
// 状态改变
handleStatus(row) {
updateProjectStatus({ id: row.id, status: row.status }).then(res => {
this.$message({ type: 'success', message: '修改成功' })
})
}
}
}
</script>
...@@ -4,58 +4,58 @@ import httpRequest from '@/utils/axios' ...@@ -4,58 +4,58 @@ import httpRequest from '@/utils/axios'
* 获取角色列表 * 获取角色列表
*/ */
export function getRoleList(params) { export function getRoleList(params) {
return httpRequest.get('/api/xedu/admin/v1/roles', { params }) return httpRequest.get('/api/xedu/admin/v2/roles', { params })
} }
/** /**
* 获取角色详情 * 获取角色详情
*/ */
export function getRole(params) { export function getRole(params) {
return httpRequest.get(`/api/xedu/admin/v1/role/${params.id}`, { params }) return httpRequest.get(`/api/xedu/admin/v2/role/${params.id}`, { params })
} }
/** /**
* 创建角色 * 创建角色
*/ */
export function createRole(data) { export function createRole(data) {
return httpRequest.post('/api/xedu/admin/v1/role', data) return httpRequest.post('/api/xedu/admin/v2/role', data)
} }
/** /**
* 修改角色 * 修改角色
*/ */
export function updateRole(data) { export function updateRole(data) {
return httpRequest.put(`/api/xedu/admin/v1/role/${data.id}`, data) return httpRequest.put(`/api/xedu/admin/v2/role/${data.id}`, data)
} }
/** /**
* 删除角色 * 删除角色
*/ */
export function deleteRole(data) { export function deleteRole(data) {
return httpRequest.delete(`/api/xedu/admin/v1/role/${data.id}`, data) return httpRequest.delete(`/api/xedu/admin/v2/role/${data.id}`, data)
} }
/** /**
* 获取权限列表 * 获取权限列表
*/ */
export function getPermissionList(params) { export function getPermissionList(params) {
return httpRequest.get('/api/xedu/admin/v1/permissions', { params }) return httpRequest.get('/api/xedu/admin/v2/permissions', { params })
} }
/** /**
* 获取角色所有权限 * 获取角色所有权限
*/ */
export function getRolePermissions(params) { export function getRolePermissions(params) {
return httpRequest.get(`/api/xedu/admin/v1/assign/${params.role_id}/permissions`, { params }) return httpRequest.get(`/api/xedu/admin/v2/assign/${params.role_id}/permissions`, { params })
} }
/** /**
* 更新角色权限 * 更新角色权限
*/ */
export function updateRolePermissions(data) { export function updateRolePermissions(data) {
return httpRequest.post('/api/xedu/admin/v1/assign/permissions-to-role', data) return httpRequest.post('/api/xedu/admin/v2/assign/permissions-to-role', data)
} }
/** /**
* 获取课程列表 * 获取课程列表
*/ */
export function getCourseList(params) { export function getCourseList(params) {
return httpRequest.get('/api/xedu/admin/v1/courses', { params }) return httpRequest.get(`/api/xedu/admin/v2/courses/${params.project_tag}`, { params })
} }
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<script> <script>
import { createRole, updateRole } from '../api.js' import { createRole, updateRole } from '../api.js'
import { pick } from 'lodash'
export default { export default {
props: { props: {
isEdit: { type: Boolean, default: false }, isEdit: { type: Boolean, default: false },
...@@ -52,20 +53,21 @@ export default { ...@@ -52,20 +53,21 @@ export default {
// 确定 // 确定
onPrimary() { onPrimary() {
this.$refs.form.validate().then(() => { this.$refs.form.validate().then(() => {
this.isEdit ? this.edit() : this.create() const params = pick(this.form, ['id', 'name', 'desc'])
this.isEdit ? this.edit(params) : this.create(params)
}) })
}, },
// 添加角色 // 添加角色
create() { create(params) {
createRole(this.form).then(res => { createRole(params).then(res => {
this.$message.success('添加成功') this.$message.success('添加成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
}) })
}, },
// 编辑角色 // 编辑角色
edit() { edit(params) {
updateRole(this.form).then(res => { updateRole(params).then(res => {
this.$message.success('修改成功') this.$message.success('修改成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
......
...@@ -56,6 +56,11 @@ export default { ...@@ -56,6 +56,11 @@ export default {
courses: [] // 所有课程 courses: [] // 所有课程
} }
}, },
computed: {
activeProject() {
return this.$store.state.activeProject || {}
}
},
async beforeMount() { async beforeMount() {
// 获取课程列表 // 获取课程列表
this.getCourses() this.getCourses()
...@@ -73,7 +78,8 @@ export default { ...@@ -73,7 +78,8 @@ export default {
getPermissions() { getPermissions() {
return getPermissionList({ is_format: 1 }).then(res => { return getPermissionList({ is_format: 1 }).then(res => {
// 移除空数组 // 移除空数组
this.permissions = JSON.parse(JSON.stringify(res.data).replaceAll(',"children":[]', '')) const permissions = JSON.parse(JSON.stringify(res.data).replaceAll(',"children":[]', ''))
this.permissions = Array.isArray(permissions) ? permissions : []
this.options = this.permissions.reduce((result, item) => { this.options = this.permissions.reduce((result, item) => {
const index = result.findIndex(i => i.system_tag === item.system_tag) const index = result.findIndex(i => i.system_tag === item.system_tag)
if (index !== -1) { if (index !== -1) {
...@@ -85,8 +91,8 @@ export default { ...@@ -85,8 +91,8 @@ export default {
}, },
// 获取课程列表 // 获取课程列表
getCourses() { getCourses() {
getCourseList().then(res => { getCourseList({ project_tag: this.activeProject.tag }).then(res => {
this.courses = res.data || [] this.courses = res.data.items || []
}) })
}, },
// 获取当前角色的权限 // 获取当前角色的权限
......
...@@ -4,40 +4,47 @@ import httpRequest from '@/utils/axios' ...@@ -4,40 +4,47 @@ import httpRequest from '@/utils/axios'
* 获取机构列表 * 获取机构列表
*/ */
export function getOrgList(params) { export function getOrgList(params) {
return httpRequest.get('/api/xedu/admin/v1/organizations', { params }) return httpRequest.get('/api/xedu/admin/v2/organizations', { params })
} }
/** /**
* 获取机构详情 * 获取机构详情
*/ */
export function getOrg(params) { export function getOrg(params) {
return httpRequest.get(`/api/xedu/admin/v1/organization/${params.id}`, { params }) return httpRequest.get(`/api/xedu/admin/v2/organization/${params.id}`, { params })
} }
/** /**
* 创建机构 * 创建机构
*/ */
export function createOrg(data) { export function createOrg(data) {
return httpRequest.post('/api/xedu/admin/v1/organization', data) return httpRequest.post('/api/xedu/admin/v2/organization', data)
} }
/** /**
* 修改机构 * 修改机构
*/ */
export function updateOrg(data) { export function updateOrg(data) {
return httpRequest.put(`/api/xedu/admin/v1/organization/${data.id}`, data) return httpRequest.put(`/api/xedu/admin/v2/organization/${data.id}`, data)
} }
/** /**
* 删除机构 * 删除机构
*/ */
export function deleteOrg(data) { export function deleteOrg(data) {
return httpRequest.delete(`/api/xedu/admin/v1/organization/${data.id}`, data) return httpRequest.delete(`/api/xedu/admin/v2/organization/${data.id}`, data)
} }
/** /**
* 获取角色列表 * 获取角色列表
*/ */
export function getRoleList(params) { export function getRoleList(params) {
return httpRequest.get('/api/xedu/admin/v1/roles', { params }) return httpRequest.get('/api/xedu/admin/v2/roles', { params })
}
/**
* 获取所有机构列表
*/
export function getAllOrgList(params) {
return httpRequest.get(`/api/xedu/admin/v2/organization/children/${params.project_id}`, { params })
} }
...@@ -24,7 +24,18 @@ ...@@ -24,7 +24,18 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="有效期" prop="validity_date"> <el-form-item label="有效期" prop="validity_date">
<el-date-picker v-model="form.validity_date" type="date" value-format="yyyy-MM-dd" :picker-options="pickerOptions" placeholder="请选择该机构有效期" /> <el-date-picker
v-model="form.validity_date"
type="date"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions"
placeholder="请选择该机构有效期"
/>
</el-form-item>
<el-form-item label="二级机构" prop="children_ids">
<el-select v-model="form.children_ids" multiple clearable placeholder="请选择二级机构" style="width: 100%">
<el-option v-for="item in orgList" :label="item.name" :value="item.id" :key="item.id" />
</el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
...@@ -35,7 +46,8 @@ ...@@ -35,7 +46,8 @@
</template> </template>
<script> <script>
import { createOrg, updateOrg, getRoleList } from '../api.js' import { createOrg, updateOrg, getRoleList, getAllOrgList } from '../api.js'
import { pick } from 'lodash'
export default { export default {
props: { props: {
isEdit: { type: Boolean, default: false }, isEdit: { type: Boolean, default: false },
...@@ -50,7 +62,8 @@ export default { ...@@ -50,7 +62,8 @@ export default {
contact_information: '', contact_information: '',
content_permissions: '', content_permissions: '',
role_id: '', role_id: '',
validity_date: '' validity_date: '',
children_ids: []
}, },
rules: { rules: {
login_mobile: [{ required: true, pattern: /^1[3-9]\d{9}$/, message: '请输入登录手机号', trigger: 'blur' }], login_mobile: [{ required: true, pattern: /^1[3-9]\d{9}$/, message: '请输入登录手机号', trigger: 'blur' }],
...@@ -58,7 +71,8 @@ export default { ...@@ -58,7 +71,8 @@ export default {
contact_name: [{ required: true, message: '请输入联系人', trigger: 'blur' }], contact_name: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
contact_information: [{ required: true, message: '请输入联系方式', trigger: 'blur' }], contact_information: [{ required: true, message: '请输入联系方式', trigger: 'blur' }],
content_permissions: [{ required: true, message: '请选择机构内容权限', trigger: 'change' }], content_permissions: [{ required: true, message: '请选择机构内容权限', trigger: 'change' }],
role_id: [{ required: true, message: '请选择机构角色', trigger: 'change' }] role_id: [{ required: true, message: '请选择机构角色', trigger: 'change' }],
validity_date: [{ required: true, message: '请选择机构有效期', trigger: 'change' }]
}, },
contentPermission: [ contentPermission: [
{ label: '付费内容', value: 1 }, { label: '付费内容', value: 1 },
...@@ -69,31 +83,45 @@ export default { ...@@ -69,31 +83,45 @@ export default {
return time.getTime() < Date.now() return time.getTime() < Date.now()
} }
}, },
roleList: [] // 角色列表 roleList: [], // 角色列表
orgList: [] // 机构列表
} }
}, },
watch: { watch: {
data: { data: {
immediate: true, immediate: true,
handler(data) { handler(data) {
this.form = Object.assign({}, this.form, data) const ids = data.children ? data.children.map(item => item.id) : []
this.form = Object.assign({}, this.form, data, { children_ids: ids })
} }
} }
}, },
computed: { computed: {
title() { title() {
return this.isEdit ? '编辑机构' : '添加机构' return this.isEdit ? '编辑机构' : '添加机构'
},
activeProject() {
return this.$store.state.activeProject || {}
} }
}, },
beforeMount() { beforeMount() {
this.getRoleList() this.getRoleList()
this.getAllOrgList()
}, },
methods: { methods: {
// 获取角色列表
getRoleList() { getRoleList() {
getRoleList({ limit: 100 }).then(res => { getRoleList({ limit: 100 }).then(res => {
this.roleList = res.data.data this.roleList = res.data.data
}) })
}, },
// 获取所有机构列表
getAllOrgList() {
getAllOrgList({ project_id: this.activeProject.id, current_id: this.data.id }).then(res => {
const orgList = Array.isArray(res.data) ? res.data : []
this.orgList = orgList.concat(this.data.children || [])
})
},
// 取消 // 取消
onCancel() { onCancel() {
this.$emit('update:visible', false) this.$emit('update:visible', false)
...@@ -101,20 +129,30 @@ export default { ...@@ -101,20 +129,30 @@ export default {
// 确定 // 确定
onPrimary() { onPrimary() {
this.$refs.form.validate().then(() => { this.$refs.form.validate().then(() => {
this.isEdit ? this.edit() : this.create() const params = pick(this.form, [
'id',
'login_mobile',
'name',
'contact_name',
'contact_information',
'role_id',
'validity_date',
'children_ids'
])
this.isEdit ? this.edit(params) : this.create(params)
}) })
}, },
// 添加机构 // 添加机构
create() { create(params) {
createOrg(this.form).then(res => { createOrg(params).then(res => {
this.$message.success('添加成功') this.$message.success('添加成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
}) })
}, },
// 编辑机构 // 编辑机构
edit() { edit(params) {
updateOrg(this.form).then(res => { updateOrg(params).then(res => {
this.$message.success('编辑成功') this.$message.success('编辑成功')
this.$emit('update:visible', false) this.$emit('update:visible', false)
this.$emit('success', res.data) this.$emit('success', res.data)
......
<template> <template>
<app-card :title="detail.name" v-loading="loading"> <div>
<template #header-aside> <app-card :title="detail.name" v-loading="loading">
<el-button type="primary" @click="visible = true">编辑</el-button> <template #header-aside>
</template> <el-button type="primary" @click="visible = true">编辑</el-button>
<el-descriptions :column="1" size="medium"> </template>
<!-- <el-descriptions-item label="机构名称">{{ detail.name }}</el-descriptions-item> --> <el-descriptions :column="1" size="medium">
<el-descriptions-item label="联系人">{{ detail.contact_name }}</el-descriptions-item> <!-- <el-descriptions-item label="机构名称">{{ detail.name }}</el-descriptions-item> -->
<el-descriptions-item label="联系方式">{{ detail.contact_information }}</el-descriptions-item> <el-descriptions-item label="联系人">{{ detail.contact_name }}</el-descriptions-item>
<!-- <el-descriptions-item label="机构内容权限"> <el-descriptions-item label="联系方式">{{ detail.contact_information }}</el-descriptions-item>
<!-- <el-descriptions-item label="机构内容权限">
<el-tag>{{ contentPermissionName(detail.content_permissions) }}</el-tag> <el-tag>{{ contentPermissionName(detail.content_permissions) }}</el-tag>
</el-descriptions-item> --> </el-descriptions-item> -->
<el-descriptions-item label="机构角色"> <el-descriptions-item label="机构角色">
<el-tag>{{ detail.role_name }}</el-tag> <el-tag>{{ detail.role_name }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="有效期"> <el-descriptions-item label="有效期">
{{ detail.validity_date }} {{ detail.validity_date }}
<el-tag v-if="detail.is_valid">未过期</el-tag> <el-tag v-if="detail.is_valid">未过期</el-tag>
<el-tag type="danger" v-else>已过期</el-tag> <el-tag type="danger" v-else>已过期</el-tag>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
<!-- 编辑 --> <!-- 编辑 -->
<editform <editform
:visible.sync="visible" :visible.sync="visible"
:isEdit="true" :isEdit="true"
:data="detail" :data="detail"
@success="handleUpdateSuccess" @success="handleUpdateSuccess"
v-if="visible" v-if="visible"
></editform> ></editform>
</app-card> </app-card>
<app-card title="二级机构">
<app-list v-bind="tableOptions" ref="list">
<template v-slot:table-status="{ row }">
<el-tag v-if="row.is_valid">未过期</el-tag>
<el-tag type="danger" v-else>已过期</el-tag>
</template>
<template v-slot:table-x="{ row }">
<router-link :to="{ path: '/school/view', query: { id: row.id } }">
<el-button plain>查看</el-button>
</router-link>
</template>
</app-list>
</app-card>
</div>
</template> </template>
<script> <script>
...@@ -47,6 +62,29 @@ export default { ...@@ -47,6 +62,29 @@ export default {
computed: { computed: {
id() { id() {
return this.$route.query.id return this.$route.query.id
},
tableOptions() {
return {
columns: [
{ label: '机构ID', prop: 'id' },
{ label: '机构名称', prop: 'name' },
{
label: '创建人',
prop: 'created_by.realname',
computed({ row }) {
return row.created_by.realname || row.created_by.username
}
},
{ label: '创建时间', prop: 'created_at' },
{
label: '状态',
prop: 'is_valid',
slots: 'table-status'
},
{ label: '操作', slots: 'table-x', align: 'right', width: '160', fixed: 'right' }
],
data: this.detail.children || []
}
} }
}, },
beforeMount() { beforeMount() {
......
...@@ -12,15 +12,21 @@ ...@@ -12,15 +12,21 @@
<el-tag type="danger" v-else>已过期</el-tag> <el-tag type="danger" v-else>已过期</el-tag>
</template> </template>
<template v-slot:table-x="{ row }"> <template v-slot:table-x="{ row }">
<router-link :to="{ path: 'school/view', query: { id: row.id } }" target="_blank" style="margin-right: 10px"> <router-link :to="{ path: '/school/view', query: { id: row.id } }" target="_blank" style="margin-right: 10px">
<el-button plain>查看</el-button> <el-button plain>查看</el-button>
</router-link> </router-link>
<el-button type="primary" plain @click="handleUpdate(row)">编辑</el-button> <el-button type="primary" plain @click="handleUpdate(row)">编辑</el-button>
<el-button type="danger" plain @click="onRemove(row)">删除</el-button> <!-- <el-button type="danger" plain @click="onRemove(row)">删除</el-button> -->
</template> </template>
</app-list> </app-list>
<!-- 创建/编辑 --> <!-- 创建/编辑 -->
<editform :visible.sync="visible" :isEdit="isEdit" :data="editRaw" @success="handleSuccess" v-if="visible"></editform> <editform
:visible.sync="visible"
:isEdit="isEdit"
:data="editRaw"
@success="handleSuccess"
v-if="visible"
></editform>
</app-card> </app-card>
</template> </template>
...@@ -103,7 +109,7 @@ export default { ...@@ -103,7 +109,7 @@ export default {
prop: 'is_valid', prop: 'is_valid',
slots: 'table-status' slots: 'table-status'
}, },
{ label: '操作', slots: 'table-x', align: 'right', width: '220', fixed: 'right' } { label: '操作', slots: 'table-x', align: 'right', width: '160', fixed: 'right' }
] ]
} }
} }
......
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import beforeEnter from '@/utils/beforeEnter'
Vue.use(VueRouter) Vue.use(VueRouter)
...@@ -10,4 +11,6 @@ const router = new VueRouter({ ...@@ -10,4 +11,6 @@ const router = new VueRouter({
routes routes
}) })
router.beforeEach(beforeEnter)
export default router export default router
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import { getUser, logout, getPermissions } from '@/api/base' import { getUser, logout, getPermissions, getAllProjects } from '@/api/base'
Vue.use(Vuex) Vue.use(Vuex)
const store = new Vuex.Store({ const store = new Vuex.Store({
state: { state: {
user: {}, user: {},
permissions: [] // 权限列表 permissions: [], // 权限列表
projects: [], // 项目列表
activeProject: {} // 当前激活的项目
}, },
mutations: { mutations: {
setUser(state, user) { setUser(state, user) {
...@@ -15,9 +17,31 @@ const store = new Vuex.Store({ ...@@ -15,9 +17,31 @@ const store = new Vuex.Store({
}, },
setPermissions(state, permissions) { setPermissions(state, permissions) {
state.permissions = permissions state.permissions = permissions
},
setProjects(state, projects) {
state.projects = projects
},
setActiveProjedct(state, project = {}) {
state.activeProject = project
localStorage.setItem('project_id', project.id || '')
} }
}, },
actions: { actions: {
// 获取所有项目列表
getAllProjects({ commit }) {
getAllProjects().then(res => {
const projects = res.data
const localProjectId = localStorage.getItem('project_id')
// 当前激活的项目
if (localProjectId) {
const activeProject = projects.find(item => item.id === localProjectId) || {}
commit('setActiveProjedct', activeProject)
}
// 所有项目列表
commit('setProjects', projects)
})
},
// 获取权限列表
getPermissions({ commit }) { getPermissions({ commit }) {
getPermissions({ type: 2 }).then(res => { getPermissions({ type: 2 }).then(res => {
if (res.data && res.data.items) { if (res.data && res.data.items) {
...@@ -57,6 +81,15 @@ const store = new Vuex.Store({ ...@@ -57,6 +81,15 @@ const store = new Vuex.Store({
} }
} }
}) })
const localProjectId = localStorage.getItem('project_id')
// 当前激活的项目
if (localProjectId) {
store.commit('setActiveProjedct', { id: localProjectId })
}
// 初始化获取权限列表
store.dispatch('getPermissions') store.dispatch('getPermissions')
// 初始化获取项目列表
store.dispatch('getAllProjects')
export default store export default store
...@@ -2,7 +2,7 @@ import axios from 'axios' ...@@ -2,7 +2,7 @@ import axios from 'axios'
import queryString from 'query-string' import queryString from 'query-string'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import router from '../router' import router from '../router'
import store from '../store'
const httpRequest = axios.create({ const httpRequest = axios.create({
timeout: 60000, timeout: 60000,
withCredentials: true withCredentials: true
...@@ -12,6 +12,9 @@ const httpRequest = axios.create({ ...@@ -12,6 +12,9 @@ const httpRequest = axios.create({
// 请求拦截 // 请求拦截
httpRequest.interceptors.request.use( httpRequest.interceptors.request.use(
function (config) { function (config) {
// 设置项目
config.params = Object.assign({ project_id: store.state.activeProject.id }, config.params)
config.data = Object.assign({ project_id: store.state.activeProject.id }, config.data)
// 权限接口单独签名 // 权限接口单独签名
// https://gitlab.ezijing.com/root/api-documents/-/blob/master/ezijing_permissions/%E7%AD%BE%E5%90%8D%E9%AA%8C%E8%AF%81.md // https://gitlab.ezijing.com/root/api-documents/-/blob/master/ezijing_permissions/%E7%AD%BE%E5%90%8D%E9%AA%8C%E8%AF%81.md
if (/^\/api\/permissions/.test(config.url)) { if (/^\/api\/permissions/.test(config.url)) {
......
...@@ -31,5 +31,11 @@ export default async function (to, from, next) { ...@@ -31,5 +31,11 @@ export default async function (to, from, next) {
return return
} }
// 选择项目
const hasProjectId = !!store.state.activeProject.id
if ((!Object.hasOwnProperty.call(to.meta, 'requireProjectId') || to.meta.requireProjectId) && !hasProjectId) {
next('/choose')
return
}
next() next()
} }
...@@ -2,31 +2,39 @@ import fs from 'fs' ...@@ -2,31 +2,39 @@ import fs from 'fs'
import path from 'path' import path from 'path'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2' import { createVuePlugin } from 'vite-plugin-vue2'
import eslint from '@rollup/plugin-eslint' import checker from 'vite-plugin-checker'
export default defineConfig({
base: process.env.BUILD_ENV === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/x-school-admin/' : '/', export default defineConfig(({ mode }) => {
plugins: [eslint({ include: '**/*.+(vue|js|jsx|ts|tsx)' }), createVuePlugin()], return {
server: { base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/x-school-admin/' : '/',
open: true, plugins: [
host: 'dev.ezijing.com', checker({
https: { eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' }
key: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.key')), }),
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem')) createVuePlugin()
],
server: {
open: true,
host: 'dev.ezijing.com',
https: {
key: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.key')),
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
},
proxy: {
'/api': 'https://x-school-admin.ezijing.com'
}
},
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src')
}
]
}, },
proxy: { css: {
'/api': 'https://x-school-admin2.ezijing.com' // 禁用SASS警告提醒
preprocessorOptions: { scss: { quietDeps: true, charset: false } }
} }
},
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src')
}
]
},
css: {
// 禁用SASS警告提醒
preprocessorOptions: { scss: { quietDeps: true } }
} }
}) })
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论