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

feat: 新增应用管理

上级 c062cbc8
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LOGIN_URL=https://login2.ezijing.com/auth/login/index
......@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="https://zws-imgs-pub.ezijing.com/pc/base/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>紫荆后台管理系统模板</title>
<title>紫荆权限管理系统</title>
</head>
<body>
<div id="app"></div>
......
$--color-primary: #3276fc;
$--color-info: #3c4043;
// border
$--border-radius-small: 8px !default;
// dialog
$--message-close-size: 20px !default;
/* 改变 icon 字体路径变量,必需 */
$--font-path: 'element-ui/lib/theme-chalk/fonts';
......
......@@ -190,23 +190,23 @@ export default {
let params = this.params
// 翻页参数设置
if (this.hasPagination) {
params.page = (this.page.currentPage - 1).toString()
params.page_size = this.page.size.toString()
params.page = this.page.currentPage
params.limit = this.page.size
}
// 接口请求之前
if (beforeRequest) {
params = beforeRequest(params, isReset)
}
// for (const key in params) {
// if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
// delete params[key]
// }
// }
for (const key in params) {
if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
delete params[key]
}
}
this.loading = true
httpRequest(params)
.then(res => {
const { data = [], page_info: pageInfo = {} } = res || {}
this.page.total = parseInt(pageInfo.total_number || '')
const { data = [], total = 0 } = res.data || {}
this.page.total = total
this.dataList = callback ? callback(data) : data
})
.catch(() => {
......
......@@ -30,7 +30,8 @@ export default {
icon: 'el-icon-setting',
children: [
{ name: '成员管理', path: '/settings/users' },
{ name: '角色管理', path: '/settings/roles' }
{ name: '角色管理', path: '/settings/roles' },
{ name: '权限管理', path: '/settings/permissions' }
]
}
]
......@@ -48,12 +49,13 @@ export default {
.app-aside {
position: sticky;
top: 0;
width: 240px;
width: 200px;
z-index: 100;
background: #fff;
border-right: 1px solid rgba(0, 0, 0, 0.12);
overflow-x: hidden;
overflow-y: auto;
flex: 0 0 200px;
}
.nav {
margin: 20px 0;
......
......@@ -20,10 +20,10 @@ export default {
</script>
<style lang="scss">
.app-breadcrumb {
padding: 20px 0 32px;
padding: 18px 0 32px;
.el-breadcrumb {
font-size: 24px;
font-size: 20px;
font-weight: 400;
line-height: 1;
}
......
......@@ -2,7 +2,7 @@
<div class="app-layout">
<app-header></app-header>
<div class="app-layout-container">
<app-aside></app-aside>
<app-aside v-if="sidebar"></app-aside>
<app-main></app-main>
</div>
</div>
......@@ -15,6 +15,7 @@ import AppMain from './Main.vue'
export default {
name: 'AppLayout',
props: { sidebar: { type: Boolean, default: true } },
components: { AppHeader, AppAside, AppMain }
}
</script>
......@@ -24,7 +25,7 @@ export default {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f4f5f7;
background-color: #ededed;
}
.app-layout-container {
flex: 1;
......
......@@ -24,7 +24,7 @@ export default {
.app-main {
position: relative;
flex: 1;
padding: 24px;
padding: 20px;
overflow: hidden;
}
.app-main-inner {
......
......@@ -10,7 +10,7 @@ import beforeEnter from '@/utils/beforeEnter'
// Element-UI
import ElementUI from 'element-ui'
import '@/assets/theme/style.scss'
Vue.use(ElementUI)
Vue.use(ElementUI, { size: 'small' })
// 注册模块
modules({ router, store })
......
import httpRequest from '@/utils/axios'
/**
* 获取应用列表
*/
export function getAppList(params) {
return httpRequest.get('/api/permissions/admin/v1/applications', { params })
}
/**
* 获取应用详情
*/
export function getApp(id, params) {
return httpRequest.get(`/api/permissions/admin/v1/${id}/application`, { params })
}
/**
* 创建应用
*/
export function createApp(data) {
return httpRequest.post('/api/permissions/admin/v1/application', data)
}
/**
* 更新应用
*/
export function updateApp(id, data) {
return httpRequest.put(`/api/permissions/admin/v1/${id}/application`, data)
}
/**
* 删除应用
*/
export function deleteApp(id, data) {
return httpRequest.delete(`/api/permissions/admin/v1/${id}/application`, data)
}
/**
* 更新应用签名秘钥
*/
export function updateAppSecretKey(id, data) {
return httpRequest.patch(`/api/permissions/admin/v1/${id}/application/secret-key`, 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" :hide-required-asterisk="true" label-position="top">
<el-form-item label="应用名称" prop="name">
<el-input v-model="form.name" clearable></el-input>
</el-form-item>
<el-form-item label="应用别名" prop="alias_name">
<el-input v-model="form.alias_name" clearable></el-input>
</el-form-item>
<el-form-item label="应用描述" prop="desc">
<el-input type="textarea" v-model="form.desc" clearable></el-input>
</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 { createApp, updateApp } from '../api.js'
export default {
props: {
isEdit: { type: Boolean, default: false },
data: { type: Object, default: () => ({}) }
},
data() {
return {
form: { name: '', alias_name: '', desc: '' },
rules: {
name: [{ required: true, message: '请输入应用名称', trigger: 'blur' }],
desc: [{ 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(() => {
this.isEdit ? this.edit() : this.create()
})
},
// 创建应用
create() {
createApp(this.form).then(res => {
this.$message.success('创建成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
},
// 编辑应用
edit() {
updateApp(this.form.id, this.form).then(res => {
this.$message.success('编辑成功')
this.$emit('update:visible', false)
this.$emit('success', res.data)
})
}
}
}
</script>
<style></style>
import AppLayout from '@/components/layout/Index.vue'
const routes = [
{
path: '/app',
component: AppLayout,
props: { sidebar: false },
children: [
{ path: '', component: () => import('./views/List.vue') },
{ name: 'appView', path: ':id', component: () => import('./views/Detail.vue'), props: true }
]
}
]
export { routes }
<template>
<div>
<app-card>
<div class="header">
<el-descriptions :title="detail.name" :column="1" size="medium">
<el-descriptions-item label="AppID">{{ detail.secret_id }}</el-descriptions-item>
<el-descriptions-item label="AppSecret">
{{ detail.secret_key }}
<el-link type="primary" :underline="false" @click="onReset">重置</el-link>
</el-descriptions-item>
</el-descriptions>
<el-button type="danger" @click="onRemove">删除应用</el-button>
</div>
</app-card>
<app-card>
<el-descriptions title="基本信息" :column="1" :colon="false" size="medium">
<template #extra>
<el-button type="text" @click="visible = true">修改</el-button>
</template>
<el-descriptions-item label="应用名称">{{ detail.name }}</el-descriptions-item>
<el-descriptions-item label="应用别名">{{ detail.alias_name }}</el-descriptions-item>
<el-descriptions-item label="应用描述">{{ detail.desc }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ detail.updated_at }}</el-descriptions-item>
</el-descriptions>
</app-card>
<editform :visible.sync="visible" :isEdit="true" :data="detail" @success="handleUpdateSuccess"></editform>
</div>
</template>
<script>
import AppCard from '@/components/base/AppCard.vue'
import Editform from '../components/Editform.vue'
import { getApp, deleteApp, updateAppSecretKey } from '../api.js'
export default {
props: { id: { type: String } },
components: { AppCard, Editform },
data() {
return {
visible: false,
detail: {}
}
},
beforeMount() {
this.getDetail()
},
methods: {
getDetail() {
getApp(this.id).then(res => {
this.detail = res.data
})
},
onRemove() {
this.$confirm('确定删除该应用?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(this.remove)
},
// 删除
remove() {
deleteApp(this.id).then(res => {
this.$message({ type: 'success', message: '删除成功' })
this.$router.replace('/app')
})
},
onReset() {
this.$confirm('确定重置该应用的AppSecret?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(this.reset)
},
// 重置
reset() {
updateAppSecretKey(this.id).then(() => {
this.$message({ type: 'success', message: '重置成功' })
this.getDetail()
})
},
// 更新成功
handleUpdateSuccess(data) {
this.getDetail()
}
}
}
</script>
<style scoped>
.header {
display: flex;
align-items: flex-start;
}
</style>
<template>
<app-card>
<app-list v-bind="tableOptions" ref="list">
<template #header-aside>
<el-button type="primary" @click="visible = true">创建应用</el-button>
</template>
<template v-slot:table-x="{ row }">
<el-button type="text" @click="handleView(row)">查看</el-button>
</template>
</app-list>
<editform :visible.sync="visible" @success="handleCreateSuccess"></editform>
</app-card>
</template>
<script>
import AppList from '@/components/base/AppList.vue'
import AppCard from '@/components/base/AppCard.vue'
import Editform from '../components/Editform.vue'
import { getAppList } from '../api'
export default {
components: { AppList, AppCard, Editform },
data() {
return {
visible: false
}
},
computed: {
tableOptions() {
return {
remote: { httpRequest: getAppList },
columns: [
{ label: '应用名称', prop: 'name' },
{ label: '应用别名', prop: 'alias_name' },
{ label: '应用描述', prop: 'desc' },
{ label: '操作', slots: 'table-x', align: 'right', width: 150 }
]
}
}
},
methods: {
// 创建成功刷新列表
handleCreateSuccess() {
this.$refs.list.refetch()
},
// 查看
handleView(row) {
this.$router.push({ name: 'appView', params: { id: row.id } })
}
}
}
</script>
import httpRequest from '@/utils/axios'
/**
* 获取商品列表
*/
export function getGoodsList(data) {
return httpRequest.post('/api/shop/commodity/spu/search', data)
}
const routes = [
{
path: '/settings',
component: () => import('@/components/layout/Index.vue'),
meta: { title: '设置' },
children: [
{
path: 'permissions',
component: () => import('./views/List.vue'),
meta: { title: '权限管理' }
}
]
}
]
export { routes }
<template>
<app-card>
<app-list v-bind="tableOptions" ref="list">
<template #header-aside>
<el-button type="primary">创建权限</el-button>
</template>
<template v-slot:table-x="{ row }">
<el-button type="text" @click="handleUpdate">编辑权限</el-button>
<el-button type="text">权限配置</el-button>
<el-button type="text" @click="onRemove(row)">删除</el-button>
</template>
</app-list>
</app-card>
</template>
<script>
// 组件
import AppList from '@/components/base/AppList.vue'
import AppCard from '@/components/base/AppCard.vue'
// 接口
// import { getRoleList } from '../api'
export default {
components: { AppCard, AppList },
data() {
return {}
},
computed: {
// 列表配置
tableOptions() {
return {
// remote: {
// httpRequest: getRoleList,
// params: { },
// },
columns: [
{ label: '权限名称', prop: 'name', minWidth: 140 },
{ label: '权限', prop: 'count', align: 'center', minWidth: 140 },
{ label: '操作', slots: 'table-x', align: 'right', width: 150 }
],
data: [
{
name: '管理员',
count: '4'
},
{
name: '教师',
count: '12'
},
{
name: '教务',
count: '4'
},
{
name: '助教',
count: '10'
}
]
}
}
},
methods: {
// 编辑
handleUpdate(row) {
console.log(row)
},
// 删除
onRemove() {
this.$confirm('权限删除请谨慎操作,确定删除?', '删除权限', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning'
}).then(this.handleRemove)
},
// 删除
handleRemove() {}
}
}
</script>
export default [
{
path: 'users',
component: () => import('./views/List.vue')
}
]
......@@ -92,7 +92,7 @@ export default {
methods: {
// 删除
onRemove() {
this.$confirm('商品删除请谨慎操作,确定删除?', '删除商品', {
this.$confirm('成员删除请谨慎操作,确定删除?', '删除成员', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning'
......
......@@ -3,7 +3,7 @@ import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [{ path: '*', redirect: '/test' }]
const routes = [{ path: '*', redirect: '/app' }]
const router = new VueRouter({
mode: 'history',
......
import axios from 'axios'
import queryString from 'query-string'
import { Message } from 'element-ui'
import router from '../router'
const httpRequest = axios.create({
timeout: 60000,
withCredentials: true
withCredentials: true,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
// 请求拦截
httpRequest.interceptors.request.use(
function (config) {
// 默认参数
const defaultHeaders = {
timestamp: parseInt(Date.now() / 1000),
nonce: Math.random().toString(36).slice(-8),
'secret-id': 'ezijing_20200410',
'secret-key': 'THIxz9hfbMDD5pil',
signature: 'UG7wBenexQhiuD2wpCwuxkU0jqcj006d'
}
config.headers = Object.assign(config.headers, defaultHeaders)
if (config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
config.data = queryString.stringify(config.data)
}
......@@ -31,10 +42,20 @@ httpRequest.interceptors.request.use(
httpRequest.interceptors.response.use(
function (response) {
const { data } = response
if (data.code) {
return Promise.reject(data)
}
// 正常返回
if (data.code === 0) {
return data
}
// 未登录
if (data.code === 4001) {
window.location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(window.location.href)}`
}
// 没有权限
if (data.code === 4008) {
router.push('/401')
}
Message({ message: data.message, type: 'error' })
return Promise.reject(data)
},
function (error) {
if (error.response) {
......@@ -45,11 +66,10 @@ httpRequest.interceptors.response.use(
} else {
Message.error(message || error.response.data)
}
return Promise.reject(error.response)
} else {
console.log(error)
}
return Promise.reject(error)
return Promise.reject(error.response || error)
}
)
......
......@@ -14,7 +14,7 @@ export default defineConfig({
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
},
proxy: {
'/api': 'https://project-api.ezijing.com'
'/api': 'https://app2.ezijing.com'
}
},
resolve: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论