This commit is contained in:
ky_sunl
2021-04-15 02:19:27 +00:00
parent 3ed8fdfc43
commit 8d24064199
19 changed files with 698 additions and 141 deletions

View File

@@ -4,6 +4,7 @@
@import './lib/align.less';
@import './lib/font-size.less';
@import './lib/margin.less';
@import './lib/width-height.less';
@import './lib/scrollbar.less';
@import './main.less';
@import './frame/dark.less';

View File

@@ -136,3 +136,24 @@
}
}
}
.yo-modal-form {
.ant-modal-body {
padding: 0;
}
.yo-form {
>h3 {
margin-top: @padding-sm;
padding: 0 @padding-md;
}
.yo-form-group {
margin-bottom: 0;
}
.ant-form-item {
border-right: 0;
border-left: 0;
&:first-child {
margin-top: -1px;
}
}
}
}

View File

@@ -46,3 +46,37 @@
}
}
}
.yo-table {
.border-right-none {
border-right-width: 0;
&:last-child {
border-right-width: 1px;
}
}
.ant-table-content {
>.ant-table-body {
>table {
>.ant-table-thead {
>tr {
>th {
.border-right-none();
}
}
}
>.ant-table-tbody {
>tr {
>td {
.border-right-none();
}
}
}
}
}
}
}
.yo-table-actions {
display: inline-block;
>a {
color: darken(@primary-color, 20%);
}
}

View File

@@ -0,0 +1,3 @@
.w-100-p {
width: 100%;
}

View File

@@ -0,0 +1,3 @@
.w-100-p {
width: 100%;
}

View File

@@ -61,25 +61,27 @@ for (let key in urls) {
}
}
api[`${key}E`] = function (params = {}) {
return initInstance()[method](url, params)
api[`${key}Wait`] = function (params = {}) {
if (method === 'post') {
return initInstance().post(url, params)
} else {
return initInstance().get(url, {
params
})
}
}
api[key] = function (params = {}) {
return new Promise((reslove, reject) => {
api[`${key}E`](params)
api[`${key}Wait`](params)
.then(({ data }) => {
if (data.code === status.OK) {
reslove(data)
} else {
reject(data)
}
reslove(data)
})
.catch(err => {
if (process.env.VUE_APP_NODE_ENV === 'development') {
alert('发生错误,请联系管理员')
} else {
console.warn(err, urls[key])
reject(err, urls[key])
}
})
})
@@ -94,10 +96,10 @@ for (let key in urls) {
*/
api.$queue = function (queue) {
return new Promise((reslove) => {
axios.all(queue).then(axios.spread(function () {
const res = Array.prototype.slice.apply(arguments).map(p => p.data)
axios.all(queue).then((results) => {
const res = results.map(p => p.data)
reslove(res)
}))
})
})
}

View File

@@ -0,0 +1,7 @@
export default {
getAppPage: ['/sysApp/page', 'get'],
sysAppAdd: '/sysApp/add',
sysAppEdit: '/sysApp/edit',
sysAppDelete: '/sysApp/delete',
sysAppSetAsDefault: '/sysApp/setAsDefault'
}

View File

@@ -0,0 +1,3 @@
export default {
sysDictTypeDropDown: ['/sysDictType/dropDown', 'get'],
}

View File

@@ -11,12 +11,12 @@
* [url]必填,为接口地址,[method]选填,为请求方式(不区分大小写),默认为POST
*/
import loginManage from './loginManage'
import dictManage from './dictManage'
import appManage from './appManage'
export default {
login: '/login',
logout: ['/logout', 'get'],
getLoginUser: ['/getLoginUser', 'get'],
getMenu: '/menu/get',
test: '/gate/allownullapi'
...loginManage,
...dictManage,
...appManage
}

View File

@@ -0,0 +1,5 @@
export default {
login: '/login',
logout: ['/logout', 'get'],
getLoginUser: ['/getLoginUser', 'get'],
}

View File

@@ -16,12 +16,12 @@
* 此时验证的是同时拥有"sysApp:page"和"sysApp:add"两项权限才会渲染
* 2.3.或者关系多项权限
* 例: :auth="[['sysApp:page', 'sysApp:add'], ['sysApp:edit']]"
* 等同于: :auth="[['sysApp:page', 'sysApp:add'], 'sysApp:edit']"
* 二维数组结构,内部数组之间为并且关系
* 此时验证的是"sysApp:page"&"sysApp:add"||"sysApp:edit"
* 注意:或者的条件必须包括在数组中,暴露在外则判定为并且
* 2.4.可直接传入布尔值
* 例: :auth="['sysApp:page', 1 === 1]"
* :auth="[['sysApp:page', 'sysApp:add'], 1 === 1]"
* :auth="[['sysApp:page', 'sysApp:add'], [1 === 1]]"
*
* 3.Json
* 如果觉得多项权限时每次都要写应用编号比较繁琐,可对Array形式进行简化
@@ -31,12 +31,81 @@
* 例: :auth="{ sysApp: ['page', 'add'] }"
* 3.3.或者关系多项权限
* 例: :auth="{ sysApp: [['page', 'add'], ['edit']]}"
* 等同于: :auth="{ sysApp: [['page', 'add'], 'edit']}"
* 3.4.可直接传入布尔值
* 例: :auth="{ sysApp: ['page', 1 === 1] }"
* :auth="{ sysApp: [['page', 'add'], 1 === 1] }"
* :auth="{ sysApp: [['page', 'add'], [1 === 1]] }"
*
*/
const authByArray = (auth, permissions) => {
let result = true
const flags = []
auth.forEach(p => {
switch (p.constructor) {
case String:
flags.push([permissions.indexOf(p) > -1, '&&'])
break
case Array:
flags.push([authByArray(p, permissions), '||'])
break
case Boolean:
flags.push([p, '&&'])
break
}
})
flags.forEach(p => {
result = p[1] === '&&' ? result && p[0] : result || p[0]
})
return result
}
const authByJson = (auth, permissions) => {
let result = true
const flags = []
const deepName = (arr, key) => {
arr.forEach(p => {
switch (p.constructor) {
case String:
p = `${key}:${p}`
break
case Array:
p = deepName(p, key)
break
default:
p = p
break
}
})
}
for (let key in auth) {
const app = auth[key]
switch (app.constructor) {
case String:
flags.push(permissions.indexOf(`${key}:${p}`) > -1)
break
case Array:
flags.push(authByArray(deepName(app, key), permissions))
break
}
}
flags.forEach(p => {
result = result && p
})
return result
}
export default {
functional: true,
props: {
@@ -44,7 +113,7 @@ export default {
default() {
return new Array()
},
type: Array,
type: [Array, Object, String],
},
authExclude: {
default() {
@@ -53,42 +122,45 @@ export default {
type: Array,
},
},
render(h, context) {
const { props, scopedSlots } = context
const global = context.parent.$root.global
const { info } = context.parent.$root.global
const permissions = info.permissions
const auth = props.auth
const authExclude = props.authExclude
if (!global.info) {
if (!info) {
return false
}
/**
* 超级管理员
*/
// if (global.info.adminType === 1) {
// return scopedSlots.default()
// }
if (info.adminType === 1) {
return scopedSlots.default()
}
if (auth.length) {
for (let i = 0; i < auth.length; i++) {
if (global.info && (auth[i] & global.info.authority) === auth[i]) {
return scopedSlots.default()
}
}
} else if (authExclude.length) {
let flag = false
for (let i = 0; i < authExclude.length; i++) {
if (global.info && (authExclude[i] & global.info.authority) === authExclude[i]) {
flag = true
}
}
let flag = false
if (!flag) {
return scopedSlots.default()
if (auth) {
switch (auth.constructor) {
case String:
flag = permissions.indexOf(auth) > -1
break
case Array:
flag = authByArray(auth, permissions)
break
case Object:
flag = authByJson(auth, permissions)
break
}
}
if (flag) {
return scopedSlots.default()
}
return false
},
}

View File

@@ -0,0 +1,105 @@
export default {
props: {
pageNo: {
default: 1,
type: Number,
},
pageSize: {
default: 20,
type: Number,
},
loadData: {
type: Function,
require: true,
},
columns: {
type: Array,
require: true,
},
},
data() {
return {
loading: false,
data: [],
pagination: {
current: this.pageNo,
pageSize: this.pageSize,
total: 0,
size: 'small',
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `总共${total}条数据`
},
};
},
created() {
this.onLoadData()
},
methods: {
onLoading() {
this.loading = {
indicator: <a-icon type="loading" spin />
}
},
onLoaded() {
this.loading = false
},
onLoadData() {
this.onLoading()
this.loadData({
pageNo: this.pagination.current,
pageSize: this.pagination.pageSize
}).then((res) => {
this.data = res.rows
this.pagination.total = res.totalRows
this.onLoaded()
})
},
onReloadData(refresh = false) {
if (refresh) {
this.pagination.current = this.pageNo
this.pagination.pageSize = this.pageSize
}
this.onLoadData()
},
onTableChange(pagination, filters, sorter) {
this.pagination = pagination
this.onLoadData()
}
},
render() {
const props = {
loading: this.loading,
pagination: this.pagination,
dataSource: this.data,
columns: this.columns,
bordered: true,
size: 'middle',
rowKey: record => record.id
}
const on = {
change: this.onTableChange
}
return (
<a-table class="yo-table" {...{ props, on, scopedSlots: { ...this.$scopedSlots } }}>
{Object.keys(this.$slots).map((name) => (
<template slot={name}>{this.$slots[name]}</template>
))}
</a-table>
)
},
}

View File

@@ -0,0 +1,21 @@
export default {
render() {
const components = []
const slots = this.$slots.default.filter(p => p.tag)
slots.forEach((p, i) => {
components.push(p)
if (i < slots.length - 1) {
components.push(<a-divider type="vertical" />)
}
})
return (
<div class="yo-table-actions">
{components}
</div>
)
}
}

View File

@@ -0,0 +1,69 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="新增应用"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口
*/
onOpen() {
this.visible = true;
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api.sysAppAdd(this.$refs['form-body'].form).then(({ success, message }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
} else {
this.$message.error(message);
}
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -0,0 +1,72 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="编辑应用"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(() => {
this.$refs['form-body'].onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api.sysAppEdit(this.$refs['form-body'].form).then(({ success, message }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('新增成功');
this.onCancel();
this.$emit('ok');
} else {
this.$message.error(message);
}
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -0,0 +1,63 @@
<template>
<a-form-model :model="form" :rules="rules" class="yo-form" ref="form">
<div class="yo-form-group">
<a-form-model-item label="应用名称" prop="name">
<a-input placeholder="请输入应用名称" v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="排序" prop="sort">
<a-input-number
:max="1000"
:min="0"
class="w-100-p"
placeholder="请输入排序"
v-model="form.sort"
/>
</a-form-model-item>
</div>
</a-form-model>
</template>
<script>
export default {
data() {
return {
form: {
active: 'N',
},
rules: {
name: [{ required: true, message: '请输入应用名称' }],
code: [{ required: true, message: '请输入唯一编码' }],
},
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(record) {
this.form = this.$_.cloneDeep(record);
},
/**
* 必要的方法
* 在外部窗口进行保存时调用表单验证
*/
onValidate(callback) {
this.$refs.form.validate(callback);
},
/**
* 必要的方法
* 在外部窗口关闭或重置时对表单验证进行初始化
*/
onResetFields() {
setTimeout(() => {
this.$refs.form.resetFields();
}, 300);
},
},
};
</script>

View File

@@ -2,7 +2,7 @@
<container>
<br />
<a-card :bordered="false">
<Auth :auth="{ sysApp: ['page'] }">
<Auth auth="sysApp:page">
<div class="yo-query-bar">
<a-form-model :model="query" layout="inline">
<a-form-model-item label="应用名称">
@@ -12,131 +12,208 @@
<a-input placeholder="请输入唯一编码" v-model="query.code" />
</a-form-model-item>
<a-form-model-item>
<a-button html-type="submit" type="primary">查询</a-button>
<a-button-group>
<a-button @click="onQuery" type="primary">查询</a-button>
<a-button @click="() => { query = {}, onQuery() }">重置</a-button>
</a-button-group>
</a-form-model-item>
</a-form-model>
</div>
</Auth>
<a-table
:bordered="true"
:columns="columns"
:data-source="data"
:pagination="{ pageSize: 20}"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
>
<yo-table :columns="columns" :load-data="loadData" ref="table">
<div class="yo-action-bar" slot="title">
<div class="yo-action-bar--actions">
<a-button>Button</a-button>
<a-button>Button</a-button>
<a-button :disabled="true">Button</a-button>
<a-button>Button</a-button>
<a-button-group>
<a-button>Button</a-button>
<a-button>Button</a-button>
<a-button>Button</a-button>
</a-button-group>
<a-dropdown>
<a-menu slot="overlay">
<a-menu-item key="1">1st item</a-menu-item>
<a-menu-item key="2">2nd item</a-menu-item>
<a-menu-item key="3">3rd item</a-menu-item>
</a-menu>
<a-button>
Actions
<a-icon type="down" />
</a-button>
</a-dropdown>
<a-dropdown-button>
Dropdown
<a-menu slot="overlay">
<a-menu-item key="1">
<a-icon type="user" />1st menu item
</a-menu-item>
<a-menu-item key="2">
<a-icon type="user" />2nd menu item
</a-menu-item>
<a-menu-item key="3">
<a-icon type="user" />3rd item
</a-menu-item>
</a-menu>
</a-dropdown-button>
<a-button @click="onOpen('add-form')">新增应用</a-button>
</div>
</div>
</a-table>
<span slot="active" slot-scope="text, record">
{{ bindCodeValue(text, 'yes_or_no') }}
<Auth auth="sysApp:setAsDefault" v-if="record.active == 'N'">
<div class="yo-table-actions">
<a-divider type="vertical" />
<a-popconfirm
@confirm="onSetDefault(record)"
placement="topRight"
title="是否确认设置为默认应用"
>
<a>设为默认</a>
</a-popconfirm>
</div>
</Auth>
</span>
<span slot="status" slot-scope="text">{{ bindCodeValue(text, 'common_status') }}</span>
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="sysApp:edit">
<a @click="onOpen('edit-form', record)">编辑</a>
</Auth>
<Auth auth="sysApp:delete">
<a-popconfirm @confirm="onDelete(record)" placement="topRight" title="是否确认删除">
<a>删除</a>
</a-popconfirm>
</Auth>
</yo-table-actions>
</span>
</yo-table>
</a-card>
<br />
<add-form @ok="onReloadData" ref="add-form" />
<edit-form @ok="onReloadData" ref="edit-form" />
</container>
</template>
<script>
const _data = [
{
area: '海曙区',
title: '曙光电影院地块',
count: 13,
date: '2021-01-01',
},
{
area: '江北区',
title: '大庆新村地块旧城区改建项目',
count: 322,
date: '2021-01-01',
},
{
area: '宁海县',
title: '桥头胡街道旧城区改造华驰文教地块',
count: 1,
date: '2021-01-01',
},
{
area: '慈溪市',
title: '七二三南延道路工程',
count: 1,
date: '2021-01-01',
},
{
area: '北仑区',
title: '原粮食局宿舍楼1号、2号楼太河路北延工程',
count: 32,
date: '2021-01-01',
},
];
import YoTable from '@/components/yoTable';
import YoTableActions from '@/components/yoTableActions';
const data = Object.assign([], _data, _data, _data);
data.map((p, i) => (p.key = 'abcdefghijklmnopqrstuvwxyz'[i]));
import AddForm from './addForm';
import EditForm from './editForm';
export default {
components: {
YoTable,
YoTableActions,
AddForm,
EditForm,
},
data() {
return {
query: {
area: '宁波市',
year: '2021',
},
query: {},
columns: [
{
title: '区域',
dataIndex: 'area',
title: '应用名称',
dataIndex: 'name',
},
{
title: '项目名称',
dataIndex: 'title',
title: '唯一编码',
dataIndex: 'code',
},
{
title: '户数',
dataIndex: 'count',
title: '是否默认',
dataIndex: 'active',
scopedSlots: {
customRender: 'active',
},
},
{
title: '时间',
dataIndex: 'date',
title: '状态',
dataIndex: 'status',
scopedSlots: {
customRender: 'status',
},
},
{
title: '排序',
dataIndex: 'sort',
},
{
title: '操作',
width: '200px',
dataIndex: 'action',
scopedSlots: {
customRender: 'action',
},
},
],
codes: [
{
code: 'yes_or_no',
values: [],
},
{
code: 'common_status',
values: [],
},
],
data,
selectedRowKeys: [],
};
},
created() {
this.onLoadCodes();
},
methods: {
onSelectChange(selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys;
/**
* 必要的方法
* 传给yo-table以示意数据接口及其参数和返回的数据结构
*/
loadData(params) {
return this.$api
.getAppPage({
...params,
...this.query,
})
.then((res) => {
return res.data;
});
},
/**
* 有查询功能时的必要方法
* 加载数据时初始化分页信息
*/
onQuery() {
this.$refs.table.onReloadData(true);
},
/**
* 必要方法
* 重新列表数据
*/
onReloadData() {
this.$refs.table.onReloadData();
},
/**
* 加载字典数据时的必要方法
*/
onLoadCodes() {
this.$api
.$queue([
this.$api.sysDictTypeDropDownWait({ code: 'yes_or_no' }),
this.$api.sysDictTypeDropDownWait({ code: 'common_status' }),
])
.then(([yesOrNo, commonStatus]) => {
this.codes.find((p) => p.code === 'yes_or_no').values = yesOrNo.data;
this.codes.find((p) => p.code === 'common_status').values = commonStatus.data;
});
},
bindCodeValue(code, name) {
const c = this.codes.find((p) => p.code == name).values.find((p) => p.code == code);
if (c) {
return c.value;
}
return null;
},
/**
* 有编辑新增功能的必要方法
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
this.$refs[formName].onOpen(record);
},
onResult(success, message, successMessage) {
if (success) {
this.$message.success(successMessage);
this.onReloadData();
} else {
this.$refs.table.onLoaded();
this.$message.error(message);
}
},
onSetDefault(record) {
this.$refs.table.onLoading();
this.$api.sysAppSetAsDefault({ id: record.id }).then(({ success, message }) => {
this.onResult(success, message, '设置成功');
});
},
onDelete(record) {
this.$refs.table.onLoading();
this.$api.sysAppDelete(record).then(({ success, message }) => {
this.onResult(success, message, '删除成功');
});
},
},
};

View File

@@ -44,7 +44,7 @@ export default {
type: Array,
},
tabActived: {
type: String,
type: [String, Number],
},
},
data() {

View File

@@ -55,7 +55,6 @@ import Content from './_layout/content';
import Setting from './setting';
import { api } from '@/common/api';
import { setGlobal } from '@/common/login';
const getNewID = () => {
@@ -89,7 +88,7 @@ export default {
},
created() {
api.getLoginUser().then(({ data }) => {
this.$api.getLoginUser().then(({ data }) => {
const info = { ...data };
delete info.apps;
delete info.menus;