parent
2152d89d27
commit
80b159d3c0
@ -0,0 +1,34 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/api/auth/login',
|
||||
method: 'post',
|
||||
data,
|
||||
isLoading: false
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo(token) {
|
||||
return request({
|
||||
url: '/api/auth/me',
|
||||
method: 'post',
|
||||
params: { token },
|
||||
isLoading: false
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/api/auth/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function permissions () {
|
||||
return request({
|
||||
url: "/api/auth/permissions",
|
||||
method: "post",
|
||||
isLoading: false,
|
||||
})
|
||||
}
|
||||
@ -1,34 +1,41 @@
|
||||
import request from '@/utils/request'
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function login(data) {
|
||||
export function index (params) {
|
||||
return request({
|
||||
url: '/api/auth/login',
|
||||
method: 'post',
|
||||
data,
|
||||
isLoading: false
|
||||
url: "/api/backend/user",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo(token) {
|
||||
export function save (data) {
|
||||
return request({
|
||||
url: '/api/auth/me',
|
||||
method: 'post',
|
||||
params: { token },
|
||||
isLoading: false
|
||||
url: "/api/backend/user/save",
|
||||
method: "post",
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function destroy (data) {
|
||||
return request({
|
||||
url: "/api/backend/user/delete",
|
||||
method: "post",
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
export function getRoles (params) {
|
||||
return request({
|
||||
url: '/api/auth/logout',
|
||||
method: 'post'
|
||||
url: "/api/backend/user/get-roles",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function permissions () {
|
||||
export function setRoles (data) {
|
||||
return request({
|
||||
url: "/api/auth/permissions",
|
||||
url: "/api/backend/user/set-roles",
|
||||
method: "post",
|
||||
isLoading: false,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Cookies from "js-cookie"
|
||||
const version = require('element-ui/package.json').version // element-ui version from node_modules
|
||||
const ORIGINAL_THEME = '#409EFF' // default color
|
||||
let index = 0;
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
chalk: '', // content of theme-chalk css
|
||||
theme: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.theme = Cookies.get("defaultTheme")
|
||||
document.body.style.setProperty('--theme-color', Cookies.get("defaultTheme"));
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
async theme(val) {
|
||||
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
|
||||
if (typeof val !== 'string') return
|
||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
||||
console.log(themeCluster, originalCluster)
|
||||
let $message
|
||||
if (index !== 0) {
|
||||
$message = this.$message({
|
||||
message: '切换主题中',
|
||||
customClass: 'theme-message',
|
||||
type: 'success',
|
||||
duration: 0,
|
||||
iconClass: 'el-icon-loading'
|
||||
})
|
||||
}
|
||||
|
||||
const getHandler = (variable, id) => {
|
||||
return () => {
|
||||
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
|
||||
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
|
||||
|
||||
let styleTag = document.getElementById(id)
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.setAttribute('id', id)
|
||||
document.head.appendChild(styleTag)
|
||||
}
|
||||
styleTag.innerText = newStyle
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.chalk) {
|
||||
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
|
||||
await this.getCSSString(url, 'chalk')
|
||||
}
|
||||
|
||||
const chalkHandler = getHandler('chalk', 'chalk-style')
|
||||
|
||||
chalkHandler()
|
||||
|
||||
const styles = [].slice.call(document.querySelectorAll('style'))
|
||||
.filter(style => {
|
||||
const text = style.innerText
|
||||
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
|
||||
})
|
||||
styles.forEach(style => {
|
||||
const { innerText } = style
|
||||
if (typeof innerText !== 'string') return
|
||||
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
|
||||
})
|
||||
|
||||
this.$emit('change', val)
|
||||
Cookies.set("defaultTheme", val)
|
||||
document.body.style.setProperty('--theme-color', val);
|
||||
index++;
|
||||
$message?.close()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateStyle(style, oldCluster, newCluster) {
|
||||
let newStyle = style
|
||||
oldCluster.forEach((color, index) => {
|
||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
||||
})
|
||||
return newStyle
|
||||
},
|
||||
|
||||
getCSSString(url, variable) {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
xhr.open('GET', url)
|
||||
xhr.send()
|
||||
})
|
||||
},
|
||||
|
||||
getThemeCluster(theme) {
|
||||
const tintColor = (color, tint) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) { // when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
}
|
||||
|
||||
const shadeColor = (color, shade) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
red = Math.round((1 - shade) * red)
|
||||
green = Math.round((1 - shade) * green)
|
||||
blue = Math.round((1 - shade) * blue)
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
|
||||
const clusters = [theme]
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
|
||||
}
|
||||
clusters.push(shadeColor(theme, 0.1))
|
||||
return clusters
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-message,
|
||||
.theme-picker-dropdown {
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="always">
|
||||
<template #header>
|
||||
<slot name="header">
|
||||
<div>
|
||||
<Icon :icon="$route.meta.icon"></Icon>
|
||||
<span style="font-weight: 600;letter-spacing: 1px;">{{ $route.meta.title }}</span>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<slot></slot>
|
||||
</template>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Icon from "@/layout/components/Navbar/Icon.vue"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
computed: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-modal :value="isShow"
|
||||
show-footer
|
||||
title="权限菜单"
|
||||
show-confirm-button
|
||||
:width="600"
|
||||
:height="600"
|
||||
esc-closable
|
||||
@input="e => $emit('update:isShow',e)">
|
||||
<el-form ref="elForm" :model="form" :rules="rules" label-position="top" label-width="100">
|
||||
<el-form-item label="父级菜单" prop="pid" required>
|
||||
<Treeselect v-model="form.pid"
|
||||
:options="formatList"
|
||||
noChildrenText="无子菜单"
|
||||
:normalizer="node => ({
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children,
|
||||
isDefaultExpanded: true
|
||||
})"></Treeselect>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name" required>
|
||||
<el-input v-model="form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input v-model="form.icon" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="路由路径" prop="path" required>
|
||||
<el-input v-model="form.path" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="api前缀" prop="api_profix" required>
|
||||
<el-input v-model="form.api_profix" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否可见" prop="visible">
|
||||
<el-switch v-model="form.visible" :active-value="1" :inactive-value="0" active-text="显示" inactive-text="隐藏"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sortnumber">
|
||||
<el-input-number controls-position="right" :precision="0" v-model="form.sortnumber"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submit">确认</el-button>
|
||||
</template>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { save } from "@/api/menu"
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
name: "",
|
||||
pid: 0,
|
||||
icon: "",
|
||||
visible: 1,
|
||||
path: "",
|
||||
api_profix: "",
|
||||
sortnumber: 0
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: "请输入名称" }
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: "请输入路由路径" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (/^\/.|^#+/.test(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error("请以'/'开头或为'#'"))
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
api_profix: [
|
||||
{ required: true, message: "请输入api前缀" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setPid (pid) {
|
||||
this.form.pid = pid;
|
||||
},
|
||||
|
||||
submit () {
|
||||
this.$refs["elForm"].validate(async valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
try {
|
||||
await save(this.form)
|
||||
this.$message.success("新增成功")
|
||||
this.$emit('refresh')
|
||||
this.$emit('update:isShow',false)
|
||||
this.loading = false
|
||||
this.$refs["elForm"].resetFields()
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formatList () {
|
||||
return [
|
||||
{
|
||||
name: "#根目录",
|
||||
id: 0
|
||||
},
|
||||
...this.list
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-modal :value="isShow"
|
||||
show-footer
|
||||
title="角色"
|
||||
show-confirm-button
|
||||
:width="600"
|
||||
:height="400"
|
||||
esc-closable
|
||||
@input="e => $emit('update:isShow',e)">
|
||||
<el-form ref="elForm" :model="form" :rules="rules" label-position="top" label-width="100">
|
||||
<el-form-item label="角色" prop="name" required>
|
||||
<el-input v-model="form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sortnumber">
|
||||
<el-input-number controls-position="right" :precision="0" v-model="form.sortnumber"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submit">确认</el-button>
|
||||
</template>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { save } from "@/api/role"
|
||||
export default {
|
||||
props: {
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
name: "",
|
||||
sortnumber: 0
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: "请输入角色" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit () {
|
||||
this.$refs["elForm"].validate(async valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
try {
|
||||
await save(this.form)
|
||||
this.$message.success("新增成功")
|
||||
this.$emit('refresh')
|
||||
this.$emit('update:isShow',false)
|
||||
this.loading = false
|
||||
this.$refs["elForm"].resetFields()
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-modal :value="isShow"
|
||||
show-footer
|
||||
title="角色"
|
||||
show-confirm-button
|
||||
:width="600"
|
||||
:height="600"
|
||||
esc-closable
|
||||
@input="e => $emit('update:isShow',e)">
|
||||
<el-form ref="elForm" :model="form" :rules="rules" label-position="top" label-width="100">
|
||||
<el-form-item label="姓名" prop="name" required>
|
||||
<el-input v-model="form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名" prop="username" required>
|
||||
<el-input v-model="form.username" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password" required>
|
||||
<el-input v-model="form.password" type="password" show-password clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sortnumber">
|
||||
<el-input-number controls-position="right" :precision="0" v-model="form.sortnumber"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submit">确认</el-button>
|
||||
</template>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { save } from "@/api/user"
|
||||
export default {
|
||||
props: {
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
sortnumber: 0
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: "请输入姓名" }
|
||||
],
|
||||
username: [
|
||||
{ required: true, message: "请输入用户名" }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const reg = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,20}');
|
||||
if (reg.test(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error("您的密码复杂度太低(密码中必须包含字母、数字、特殊字符)!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit () {
|
||||
this.$refs["elForm"].validate(async valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
try {
|
||||
await save(this.form)
|
||||
this.$message.success("新增成功")
|
||||
this.$emit('refresh')
|
||||
this.$emit('update:isShow',false)
|
||||
this.loading = false
|
||||
this.$refs["elForm"].resetFields()
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-modal :value="isShow"
|
||||
show-footer
|
||||
title="绑定权限"
|
||||
show-confirm-button
|
||||
:width="600"
|
||||
:height="600"
|
||||
esc-closable
|
||||
@input="e => $emit('update:isShow',e)">
|
||||
<template>
|
||||
<Treeselect v-model="form.permission_id"
|
||||
:options="tableData"
|
||||
flat
|
||||
always-open
|
||||
multiple
|
||||
clearable
|
||||
noChildrenText="无子菜单"
|
||||
:normalizer="node => ({
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children,
|
||||
isDefaultExpanded: true
|
||||
})"></Treeselect>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submit">确认</el-button>
|
||||
</template>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setPermissions, getPermissions } from "@/api/role"
|
||||
export default {
|
||||
props: {
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
|
||||
tableData: [],
|
||||
total: 0,
|
||||
form: {
|
||||
id: "",
|
||||
permission_id: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {array[string]} keys
|
||||
* @param {array[all]} values
|
||||
* @returns {void}
|
||||
*/
|
||||
setForm (keys = [],values = []) {
|
||||
keys.forEach((key, index) => {
|
||||
this.form[key] = values[index]
|
||||
})
|
||||
},
|
||||
|
||||
async getRoles () {
|
||||
const res = await getPermissions()
|
||||
this.tableData = res;
|
||||
},
|
||||
|
||||
async submit () {
|
||||
this.loading = true;
|
||||
try {
|
||||
await setPermissions(this.form)
|
||||
this.$emit("update:isShow",false)
|
||||
this.$emit("refresh")
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
created() {
|
||||
this.getRoles()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.total {
|
||||
color: #666;
|
||||
text-align: right;
|
||||
line-height: 3;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-modal :value="isShow"
|
||||
show-footer
|
||||
title="绑定角色"
|
||||
show-confirm-button
|
||||
:width="600"
|
||||
:height="600"
|
||||
esc-closable
|
||||
@input="e => $emit('update:isShow',e)">
|
||||
<template>
|
||||
<vxe-table
|
||||
stripe
|
||||
style="margin-top: 10px;"
|
||||
ref="table"
|
||||
keep-source
|
||||
show-overflow
|
||||
:column-config="{ resizable: true }"
|
||||
:checkbox-config="{ trigger: 'row', highlight: true }"
|
||||
:data="tableData">
|
||||
<vxe-column type="checkbox" width="60" fixed="left"></vxe-column>
|
||||
<vxe-column type="seq" width="58" align="center"></vxe-column>
|
||||
<vxe-column field="name" width="160" title="角色"></vxe-column>
|
||||
<vxe-column field="permissions" title="权限菜单" min-width="200" show-overflow="tooltip">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<el-tag v-for="item in row.permissions" :key="item.id">{{ item.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sortnumber" width="80" title="排序" align="center"></vxe-column>
|
||||
</vxe-table>
|
||||
<p class="total" type="primary">共 {{ total }} 条数据</p>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="loading" @click="submit">确认</el-button>
|
||||
</template>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setRoles, getRoles } from "@/api/user"
|
||||
export default {
|
||||
props: {
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
|
||||
tableData: [],
|
||||
total: 0,
|
||||
form: {
|
||||
id: "",
|
||||
role_id: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {array[string]} keys
|
||||
* @param {array[all]} values
|
||||
* @returns {void}
|
||||
*/
|
||||
setForm (keys = [],values = []) {
|
||||
keys.forEach((key, index) => {
|
||||
this.form[key] = values[index]
|
||||
})
|
||||
},
|
||||
|
||||
setSelectRow () {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs['table']) {
|
||||
this.$refs['table'].clearCheckboxRow()
|
||||
this.$refs['table'].setCheckboxRow(
|
||||
this.tableData.filter(row => this.form.role_id.findIndex(j => j === row.id) !== -1),
|
||||
true
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async getRoles () {
|
||||
const res = await getRoles()
|
||||
this.tableData = res;
|
||||
this.total = res.length;
|
||||
},
|
||||
|
||||
async submit () {
|
||||
if (this.$refs['table']) {
|
||||
this.loading = true;
|
||||
const selectRecords = this.$refs['table'].getCheckboxRecords()
|
||||
try {
|
||||
setRoles({
|
||||
id: this.form.id,
|
||||
role_id: selectRecords.map(row => row.id)
|
||||
})
|
||||
//接口中角色信息有延迟
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.$emit("update:isShow",false)
|
||||
this.$emit("refresh")
|
||||
},500)
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
"form.role_id": function(val, oldVal) {
|
||||
this.setSelectRow()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getRoles()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.total {
|
||||
color: #666;
|
||||
text-align: right;
|
||||
line-height: 3;
|
||||
}
|
||||
</style>
|
||||
@ -1,33 +1,191 @@
|
||||
<template>
|
||||
<div>
|
||||
<vxe-table
|
||||
:align="allAlign"
|
||||
:data="tableData">
|
||||
<vxe-column type="seq" width="60"></vxe-column>
|
||||
<vxe-column field="name" title="Name"></vxe-column>
|
||||
<vxe-column field="sex" title="Sex"></vxe-column>
|
||||
<vxe-column field="age" title="Age"></vxe-column>
|
||||
</vxe-table>
|
||||
<CardContainer>
|
||||
<vxe-toolbar>
|
||||
<template #buttons>
|
||||
<el-button icon="el-icon-plus" type="primary" size="small" @click="isShowAdd = true">新增</el-button>
|
||||
<el-button icon="el-icon-search" type="primary" plain size="small" @click="getList">搜索</el-button>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table
|
||||
style="margin-top: 10px;"
|
||||
ref="table"
|
||||
:loading="loading"
|
||||
keep-source
|
||||
:row-config="{ useKey: 'id', isHover: true }"
|
||||
:column-config="{ resizable: true }"
|
||||
:edit-rules="validRules"
|
||||
:edit-config="{ trigger: 'manual', mode: 'row', showStatus: true }"
|
||||
:tree-config="{ rowField: 'id', parentField: 'pid' }"
|
||||
:data="tableData">
|
||||
<vxe-column type="seq" width="58" align="center"></vxe-column>
|
||||
<vxe-column field="name" tree-node width="160" title="菜单" :edit-render="{ name: 'input', attrs: { type: 'text'} }"></vxe-column>
|
||||
<vxe-column field="path" title="路由地址" min-width="140" :edit-render="{ name: 'input', attrs: { type: 'text'} }">
|
||||
<template #default="{ row }">
|
||||
<template v-if="/#/g.test(row.path)">
|
||||
<SvgIcon icon-class="folder" class-name="icon-folder"></SvgIcon>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ row.path }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="api_profix" min-width="140" title="api前缀" :edit-render="{ name: 'input', attrs: { type: 'text'} }">
|
||||
<template #default="{ row }">
|
||||
<template v-if="/#/g.test(row.path)">
|
||||
<SvgIcon icon-class="folder" class-name="icon-folder"></SvgIcon>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ row.path }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="visible" width="100" align="center" title="是否显示" :edit-render="{ name: 'select', options: [{ value: 1, label: '显示' },{ value: 0, label: '隐藏' }] }"></vxe-column>
|
||||
<vxe-column field="icon" width="160" align="center" title="图标" :edit-render="{ name: 'input', attrs: { type: 'text'} }">
|
||||
<template #default="{ row }">
|
||||
<Icon :icon="row.icon"></Icon>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sortnumber" width="80" title="排序" align="center" :edit-render="{ name: 'input', attrs: { type: 'number' } }"></vxe-column>
|
||||
<vxe-column field="operate" title="操作" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<template v-if="isActiveStatus(row)">
|
||||
<el-button size="small" type="primary" @click="saveRowEvent(row)">保存</el-button>
|
||||
<el-button size="small" type="primary" plain @click="cancelRowEvent(row)">取消</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button size="small" type="success" @click="$refs['AddMenu'].setPid(row.id),isShowAdd = true">子菜单</el-button>
|
||||
<el-button size="small" type="warning" @click="editRowEvent(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="destroyRowEvent(row)">删除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</CardContainer>
|
||||
|
||||
<add-menu :is-show.sync="isShowAdd" :list="tableData" ref="AddMenu" @refresh="getList"></add-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addMenu from "./components/AddMenu.vue"
|
||||
import Icon from "@/layout/components/Navbar/Icon.vue"
|
||||
import SvgIcon from "@/components/SvgIcon"
|
||||
import { index, save, destroy } from "@/api/menu"
|
||||
import { deepCopy } from '@/utils'
|
||||
export default {
|
||||
components: {
|
||||
SvgIcon,
|
||||
Icon,
|
||||
addMenu
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShowAdd: false,
|
||||
|
||||
loading: false,
|
||||
allAlign: null,
|
||||
tableData: [
|
||||
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },
|
||||
{ id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
|
||||
{ id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
|
||||
{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai' }
|
||||
]
|
||||
tableData: [],
|
||||
validRules: {
|
||||
|
||||
},
|
||||
form: {
|
||||
id: "",
|
||||
pid: "",
|
||||
name: "",
|
||||
url: "",
|
||||
path: "",
|
||||
api_profix: "",
|
||||
icon: "",
|
||||
visible: 1,
|
||||
sortnumber: 0
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editRowEvent (row) {
|
||||
if (this.$refs['table']) {
|
||||
this.$refs['table'].setEditRow(row)
|
||||
}
|
||||
},
|
||||
cancelRowEvent (row) {
|
||||
if (this.$refs['table']) {
|
||||
this.$refs['table'].clearEdit().then(() => {
|
||||
// 还原行数据
|
||||
this.$refs['table'].revertData(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async getList () {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await index()
|
||||
this.tableData = res;
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async saveRowEvent (row) {
|
||||
try {
|
||||
await this.$confirm("确认保存?","提示",{
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消"
|
||||
})
|
||||
let form = deepCopy(this.form)
|
||||
for (let key in form) {
|
||||
form[key] = row[key]
|
||||
}
|
||||
this.loading = true;
|
||||
await save(form)
|
||||
await this.getList();
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async destroyRowEvent (row) {
|
||||
try {
|
||||
await this.$confirm("确认删除?","提示",{
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消"
|
||||
})
|
||||
this.loading = true;
|
||||
if (row.id) {
|
||||
await destroy({
|
||||
id: row.id
|
||||
})
|
||||
await this.getList();
|
||||
} else {
|
||||
this.tableData.splice(this.tableData.findIndex(i => i._X_ROW_KEY === row._X_ROW_KEY),1)
|
||||
}
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isActiveStatus () {
|
||||
return function (row) {
|
||||
if (this.$refs['table']) {
|
||||
return this.$refs['table'].isEditByRow(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
computed: {}
|
||||
created() {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.icon-folder {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,17 +1,210 @@
|
||||
<template>
|
||||
<div>
|
||||
<card-container>
|
||||
<vxe-toolbar>
|
||||
<template #buttons>
|
||||
<el-button icon="el-icon-plus" type="primary" size="small" @click="isShowAdd = true">新增</el-button>
|
||||
<el-button icon="el-icon-search" type="primary" plain size="small" @click="getList">搜索</el-button>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table
|
||||
stripe
|
||||
style="margin-top: 10px;"
|
||||
ref="table"
|
||||
:loading="loading"
|
||||
keep-source
|
||||
show-overflow
|
||||
:column-config="{ resizable: true }"
|
||||
:edit-rules="validRules"
|
||||
:edit-config="{ trigger: 'manual', mode: 'row', showStatus: true, isHover: true }"
|
||||
:align="allAlign"
|
||||
:data="tableData">
|
||||
<vxe-column type="seq" width="58" align="center"></vxe-column>
|
||||
<vxe-column field="name" width="140" title="姓名" :edit-render="{ name: 'input', attrs: { type: 'text'} }"></vxe-column>
|
||||
<vxe-column field="username" width="160" title="用户名" :edit-render="{ name: 'input', attrs: { type: 'text'} }"></vxe-column>
|
||||
<vxe-column field="password" width="160" title="密码" :edit-render="{ name: 'input', attrs: { type: 'password'} }">
|
||||
<template #default="{ row }">
|
||||
<span>***</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="roles" title="角色" min-width="200" show-overflow="tooltip">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<el-tag v-for="item in row.roles" :key="item.id">{{ item.name }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sortnumber" width="80" title="排序" align="center" :edit-render="{ name: 'input', attrs: { type: 'number' } }"></vxe-column>
|
||||
<vxe-column field="operate" title="操作" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<template v-if="isActiveStatus(row)">
|
||||
<el-button size="small" type="primary" @click="saveRowEvent(row)">保存</el-button>
|
||||
<el-button size="small" type="primary" plain @click="cancelRowEvent(row)">取消</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button size="small" type="success" @click="bind(row)">绑定角色</el-button>
|
||||
<el-button size="small" type="warning" @click="editRowEvent(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="destroyRowEvent(row)">删除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
|
||||
<el-pagination
|
||||
style="margin-top: 10px;"
|
||||
@size-change="e => {
|
||||
select.rows = e;
|
||||
select.page = 1;
|
||||
getList();
|
||||
}"
|
||||
@current-change="e => {
|
||||
select.page = e;
|
||||
getList();
|
||||
}"
|
||||
:current-page="select.page"
|
||||
:page-sizes="[20, 30, 40, 50]"
|
||||
:page-size="select.rows"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</card-container>
|
||||
|
||||
<AddUser :is-show.sync="isShowAdd" @refresh="getList"></AddUser>
|
||||
<BindRoles ref="BindRoles" :is-show.sync="isShowBind" @refresh="getList"></BindRoles>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BindRoles from './components/BindRoles.vue'
|
||||
import AddUser from "./components/AddUser.vue"
|
||||
import { deepCopy } from "@/utils"
|
||||
import { index, save, destroy } from "@/api/user"
|
||||
export default {
|
||||
components: {
|
||||
AddUser,
|
||||
BindRoles
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
isShowBind: false,
|
||||
isShowAdd: false,
|
||||
|
||||
select: {
|
||||
page: 1,
|
||||
rows: 20,
|
||||
keyword: ""
|
||||
},
|
||||
loading: false,
|
||||
total: 0,
|
||||
allAlign: null,
|
||||
tableData: [],
|
||||
validRules: {
|
||||
|
||||
},
|
||||
form: {
|
||||
id: "",
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
sortnumber: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
bind (row) {
|
||||
this.isShowBind = true
|
||||
this.$refs['BindRoles'].setForm(['id', 'role_id'],[row.id,row.roles?.map(i => i.id)||[]])
|
||||
},
|
||||
|
||||
editRowEvent (row) {
|
||||
if (this.$refs['table']) {
|
||||
this.$refs['table'].setEditRow(row)
|
||||
}
|
||||
},
|
||||
cancelRowEvent (row) {
|
||||
if (this.$refs['table']) {
|
||||
this.$refs['table'].clearEdit().then(() => {
|
||||
// 还原行数据
|
||||
this.$refs['table'].revertData(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async getList () {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await index(this.select)
|
||||
this.tableData = res.data;
|
||||
this.total = res.total;
|
||||
console.log(res)
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async saveRowEvent (row) {
|
||||
try {
|
||||
await this.$confirm("确认保存?","提示",{
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消"
|
||||
})
|
||||
let form = deepCopy(this.form)
|
||||
for (let key in form) {
|
||||
form[key] = row[key]
|
||||
}
|
||||
this.loading = true;
|
||||
await save(form)
|
||||
await this.getList();
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async destroyRowEvent (row) {
|
||||
try {
|
||||
await this.$confirm("确认删除?","提示",{
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消"
|
||||
})
|
||||
this.loading = true;
|
||||
if (row.id) {
|
||||
await destroy({
|
||||
id: row.id
|
||||
})
|
||||
await this.getList();
|
||||
} else {
|
||||
this.tableData.splice(this.tableData.findIndex(i => i._X_ROW_KEY === row._X_ROW_KEY),1)
|
||||
}
|
||||
this.loading = false;
|
||||
} catch (err) {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
computed: {}
|
||||
computed: {
|
||||
isActiveStatus () {
|
||||
return function (row) {
|
||||
if (this.$refs['table']) {
|
||||
return this.$refs['table'].isEditByRow(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.total {
|
||||
color: #666;
|
||||
text-align: right;
|
||||
line-height: 3;
|
||||
}
|
||||
::v-deep .el-tag + .el-tag {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in new issue