This commit is contained in:
DESKTOP-VMMLSOQ\wangzg 2024-06-26 08:19:02 +08:00
parent 49c083f6c4
commit ccd5595032
8 changed files with 564 additions and 64 deletions

View File

@ -32,7 +32,7 @@
添加 添加
</a-button> </a-button>
</div> </div>
<a-table :columns="realColumns" :data-source="tableDataSource" @change="handleChange" > <a-table :row-key="primaryKey" :columns="realColumns" :data-source="tableDataSource" @change="handleChange" >
<span slot="action" slot-scope="text, record"> <span slot="action" slot-scope="text, record">
<a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button> <a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button>
<a-divider type="vertical" /> <a-divider type="vertical" />
@ -47,21 +47,35 @@
@cancel="handleCancel" @cancel="handleCancel"
> >
<a-form class="ant-advanced-search-form" :form="editForm" @submit="handleSearch" @change="handleSearchFormChange"> <a-form class="ant-advanced-search-form" :form="editForm" @submit="handleSearch" @change="handleSearchFormChange">
<a-form-item v-for="menuItem in formItems" :key="menuItem.key" :label="menuItem.label"> <a-form-item v-for="menuItem in formItems" :key="menuItem.key" :label="menuItem.label" v-if="!menuItem.hide">
<a-input <a-input
v-decorator="[menuItem.key]" v-bind="menuItem.props || {}"
v-decorator="gtDecorator(menuItem)"
:placeholder="menuItem.placeholder || ''" :placeholder="menuItem.placeholder || ''"
v-if="menuItem.type === 'input'" v-if="menuItem.type === 'input'"
/> />
<a-select <a-select
v-decorator="[menuItem.key]" v-decorator="gtDecorator(menuItem)"
:placeholder="menuItem.placeholder || ''" :placeholder="menuItem.placeholder || ''"
v-else-if="menuItem.type === 'select'" v-else-if="menuItem.type === 'select'"
v-bind="menuItem.props || {}"
> >
<a-select-option <a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
:placeholder="menuItem.placeholder || ''" :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
</a-select> </a-select>
<a-range-picker v-decorator="[menuItem.key]" v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD" /> <a-tree-select
v-bind="menuItem.props || {}"
v-decorator="gtDecorator(menuItem)"
:placeholder="menuItem.placeholder || ''"
allow-clear
:tree-data="menuItem.children"
tree-default-expand-all
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
v-else-if="menuItem.type === 'treeSelect'"
>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
</a-tree-select>
<a-range-picker v-bind="menuItem.props || {}" v-decorator="gtDecorator(menuItem)" v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD" />
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
@ -72,6 +86,7 @@ export default {
name: "user", name: "user",
data () { data () {
return { return {
isValidate: false,
tableDataSource: [], tableDataSource: [],
form: this.$form.createForm(this, { name: `form_${new Date().valueOf()}` }), form: this.$form.createForm(this, { name: `form_${new Date().valueOf()}` }),
editForm: this.$form.createForm(this, { name: `edit_form_${new Date().valueOf()}` }), editForm: this.$form.createForm(this, { name: `edit_form_${new Date().valueOf()}` }),
@ -108,9 +123,7 @@ export default {
apiConf: { apiConf: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
} },
}, },
computed: { computed: {
searchItems () { searchItems () {
@ -136,29 +149,60 @@ export default {
this.handleSearch() this.handleSearch()
}, },
methods: { methods: {
gtDecorator (menuItem) {
if (menuItem.rules) {
this.isValidate = true
return [
menuItem.key,
{ rules: menuItem.rules }
]
}
return [
menuItem.key
]
},
handleSearchFormChange(changedFields) { handleSearchFormChange(changedFields) {
this.searchFormModel = { ...this.searchFormModel, ...changedFields } this.searchFormModel = { ...this.searchFormModel, ...changedFields }
}, },
addRow () { addRow () {
this.handleCancel() this.handleCancel()
this.$emit('edit', false)
this.dialog.visible = true this.dialog.visible = true
}, },
editRow(row) { async editRow(row) {
if (this.apiConf.detailApi) {
const {api, method, detailHandler} = this.apiConf.detailApi
const { data } = await this[`$${method}`](api, { [this.primaryKey]: row[this.primaryKey] })
this.selectRow = detailHandler && detailHandler(data) || data
} else {
this.selectRow = row this.selectRow = row
this.dialog.title = '添加' }
this.dialog.title = '编辑'
this.dialog.visible = true this.dialog.visible = true
this.dialog.isEdit = true this.dialog.isEdit = true
this.dialog.loading = false this.dialog.loading = false
this.$emit('edit', true)
setTimeout(() => {
this.editForm.setFieldsValue(this.selectRow)
}, 200)
}, },
deleteRow(row) { deleteRow(row) {
const self = this
this.$confirm({ this.$confirm({
title: '确认要删除么?', title: '确认要删除么?',
okText: '确认', okText: '确认',
okType: 'danger', okType: 'danger',
cancelText: '再想想', cancelText: '再想想',
onOk() { onOk() {
this.$post(this.apiConf.deleteApi, { const { api,deleteKey, paramsType, method } = self.apiConf.deleteApi
[this.primaryKey]: row[this.primaryKey], let params = {}
if (paramsType === 'Array') {
params = [row[self.primaryKey]]
} else {
params[deleteKey] = row[self.primaryKey]
}
self[`$${method}`](api, params, {params: params}).then(res => {
self.handleSearch()
}) })
} }
}); });
@ -177,33 +221,60 @@ export default {
async handleSearch(e) { async handleSearch(e) {
e && e.preventDefault(); e && e.preventDefault();
const values = this.form.getFieldsValue() const values = this.form.getFieldsValue()
const { data } = await this.$get(this.apiConf.listApi, { const { api, noPage, method } = this.apiConf.listApi
const { data } = await this[`$${method}`](api, {
...values, ...values,
pageSize: this.page.pageSize, pageSize: this.page.pageSize,
current: this.page.pageNo, current: this.page.pageNo,
}) })
if (noPage) {
this.tableDataSource = data
} else {
this.tableDataSource = data.items this.tableDataSource = data.items
}
}, },
async handleOk() { async handleOk() {
const values = this.editForm.getFieldsValue() if (this.isValidate) {
this.editForm.validateFields(async (err, values) => {
if (!err) {
if (this.dialog.isEdit) { if (this.dialog.isEdit) {
await this.$post(this.apiConf.editApi, { const { api, method, updateKey } = this.apiConf.editApi
[this.primaryKey]: this.selectRow[this.primaryKey], await this[`$${method}`](api, {
[updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
...values ...values
}) })
} else { } else {
debugger const { api, method } = this.apiConf.addApi
await this.$post(this.apiConf.addApi, values) await this[`$${method}`](api, values)
} }
this.handleCancel() this.handleCancel()
this.handleSearch() this.handleSearch()
}
});
} else {
const values = this.editForm.getFieldsValue()
if (this.dialog.isEdit) {
const { api, method, updateKey } = this.apiConf.editApi
await this[`$${method}`](api, {
[updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
...values
})
} else {
const { api, method } = this.apiConf.addApi
await this[`$${method}`](api, values)
}
this.handleCancel()
this.handleSearch()
}
}, },
handleCancel() { handleCancel() {
this.editRow = null this.selectRow = null
this.dialog.title = '添加' this.dialog.title = '添加'
this.dialog.visible = false this.dialog.visible = false
this.dialog.isEdit = false this.dialog.isEdit = false
this.dialog.loading = false this.dialog.loading = false
this.editForm.resetFields()
}, },
} }
} }

View File

@ -77,8 +77,14 @@ export default {
{ {
link: '/manager/role', link: '/manager/role',
title: '角色管理', title: '角色管理',
icon: 'user', icon: 'branches',
key: 'manager-role' key: 'manager-role'
},
{
link: '/manager/menu',
title: '菜单管理',
icon: 'link',
key: 'manager-menu'
} }
], ],
selectedKeys: [] selectedKeys: []
@ -90,6 +96,9 @@ export default {
handler (to) { handler (to) {
this.selectedKeys = [to.name] this.selectedKeys = [to.name]
} }
},
selectedKeys ([key]) {
this.$router.push({ name: key })
} }
}, },
computed: { computed: {

123
pages/manager/device.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<div>
<Curd
:columns="columns"
:api-conf="apiConf"
:form-items="formItems"
/>
</div>
</template>
<script>
import base from "~/templates/base";
import Curd from "../../components/smallCommon/Curd.vue";
export default {
name: "role",
extends: base,
components: {
Curd
},
data() {
return {
deviceTypes: [],
columns: [
{
dataIndex: 'name',
key: 'name',
title: '名称'
},
{
dataIndex: 'deviceMN',
key: 'deviceMN',
title: '编码'
},
{
dataIndex: 'desricption',
key: 'desricption',
title: '描述'
},
{
dataIndex: 'nickName',
key: 'nickName',
title: '别名'
}
],
apiConf: {
listApi: {api: '/api/Device/Getpage', method: 'get'},
deleteApi: {api: '/api/Device/Remove', method: 'delete', paramsType: 'Array'},
editApi: {api: '/api/Device/update', method: 'put'},
detailApi: {
api: '/api/Device/FindOne',
method: 'get',
detailHandler({role, menus}) {
}
},
addApi: {api: '/api/Device/Addd', method: 'post',}
}
}
},
computed: {
formItems() {
const self = this
return [
{
type: 'input',
key: 'deviceMN',
label: '编码',
isSearch: true,
placeholder: '请输入编码',
rules: [
{required: true, message: '请输入编码'}
]
},
{
type: 'input',
key: 'ip',
label: 'IP地址'
},
{
type: 'input',
key: 'lng',
label: '经度'
},
{
type: 'input',
key: 'lat',
label: '纬度'
},
{
type: 'input',
key: 'nickName',
label: '别名'
},
{
type: 'select',
key: 'deviceType',
label: '设备类型',
children: self.deviceTypes
},
]
}
},
created() {
this.getMenus()
},
methods: {
getMenus() {
this.$get('/api/Device/GetDeviceTypes').then(({data}) => {
this.deviceTypes = Object.keys(data).map(item => ({
label: data[item],
value: item
}))
})
}
}
}
</script>
<style scoped lang="less">
</style>

94
pages/manager/login.vue Normal file
View File

@ -0,0 +1,94 @@
<template>
<div class="login-container">
<h2>欢迎使用系统</h2>
<a-form :form="form" @submit="handleSubmit">
<a-form-item :validate-status="usernameError() ? 'error' : ''" :help="usernameError() || ''">
<a-input
v-decorator="[
'username',
{ rules: [{ required: true, message: '请输入用户名' }] },
]"
placeholder="Username"
>
<a-icon slot="prefix" type="user" style="color:rgba(0,0,0,.25)" />
</a-input>
</a-form-item>
<a-form-item :validate-status="passwordError() ? 'error' : ''" :help="passwordError() || ''">
<a-input
v-decorator="[
'password',
{ rules: [{ required: true, message: '请输入密码' }] },
]"
type="password"
placeholder="Password"
>
<a-icon slot="prefix" type="lock" style="color:rgba(0,0,0,.25)" />
</a-input>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" :disabled="hasErrors(form.getFieldsError())">
登录
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script>
function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
export default {
data() {
return {
hasErrors,
form: this.$form.createForm(this, { name: 'horizontal_login' }),
};
},
mounted() {
this.$nextTick(() => {
// To disabled submit button at the beginning.
this.form.validateFields();
});
},
methods: {
// Only show error after a field is touched.
usernameError() {
const { getFieldError, isFieldTouched } = this.form;
return isFieldTouched('username') && getFieldError('username');
},
// Only show error after a field is touched.
passwordError() {
const { getFieldError, isFieldTouched } = this.form;
return isFieldTouched('password') && getFieldError('password');
},
handleSubmit(e) {
e.preventDefault();
this.form.validateFields(async (err, values) => {
if (!err) {
const { data } = await this.$post('/api/User/login', values)
localStorage.setItem('userInfo', JSON.stringify(data))
this.$router.replace({ path: '/web1' })
}
});
},
},
};
</script>
<style scoped lang="less">
.login-container {
padding: 16px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,.2);
width: 300px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
</style>

100
pages/manager/menu.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<div>
<Curd
:columns="columns"
:api-conf="apiConf"
:form-items="formItems"
/>
</div>
</template>
<script>
import base from "~/templates/base";
import Curd from "../../components/smallCommon/Curd.vue";
export default {
name: "role",
extends: base,
components: {
Curd
},
data() {
return {
menus: [],
columns: [
{
dataIndex: 'name',
key: 'name',
title: '菜单名'
},
{
dataIndex: 'url',
key: 'url',
title: '菜单URL'
}
],
apiConf: {
addApi: {api: '/api/Menu/add', method: 'post',},
listApi: {api: '/api/Menu/list', method: 'get', noPage: true},
deleteApi: {api: '/api/Menu/remove', method: 'delete', paramsType: 'Array'},
editApi: {api: '/api/Menu/update', method: 'put'},
}
}
},
computed: {
formItems() {
const self = this
return [
{
type: 'input',
key: 'name',
label: '菜单名',
isSearch: true,
placeholder: '请输入菜单名',
rules: [
{required: true, message: '请输入菜单名'}
]
},
{
type: 'input',
key: 'url',
label: '路由',
rules: [
{required: true, message: '请输入菜单名'}
]
},
{
type: 'treeSelect',
key: 'parentId',
label: '父级菜单',
placeholder: '',
children: self.menus,
props: {
replaceFields: {
title: 'name',
value: 'id',
key: 'id',
children: 'children'
}
}
}
]
}
},
created() {
this.getMenus()
},
methods: {
getMenus() {
this.$get('/api/Menu/list').then(({data}) => {
this.menus = data
})
}
}
}
</script>
<style scoped lang="less">
</style>

View File

@ -1,5 +1,3 @@
<template> <template>
<div> <div>
<Curd <Curd
@ -12,49 +10,93 @@
<script> <script>
import base from "~/templates/base"; import base from "~/templates/base";
import Curd from "../../components/smallCommon/Curd.vue"; import Curd from "../../components/smallCommon/Curd.vue";
export default { export default {
name: "role", name: "role",
extends: base, extends: base,
components: { components: {
Curd Curd
}, },
data () { data() {
return { return {
menus: [],
columns: [ columns: [
{ {
dataIndex: 'roleName', dataIndex: 'roleName',
key: 'roleName', key: 'roleName',
title: '角色名' title: '角色名'
},
{
dataIndex: 'roleDescription',
key: 'roleDescription',
title: '角色描述'
} }
], ],
formItems: [ apiConf: {
listApi: {api: '/api/Role/all', method: 'get', noPage: true},
deleteApi: {api: '/api/Role/remove', method: 'delete', paramsType: 'Array'},
editApi: {api: '/api/Role/update', method: 'put', updateKey: 'roleId'},
detailApi: {
api: '/api/Role/get',
method: 'get',
detailHandler({role, menus}) {
return {
roleName: role.roleName,
roleDescription: role.roleDescription,
menuIds: menus.map(item => item.menuId),
id: role.id
}
}
},
addApi: {api: '/api/Role/add', method: 'post',}
}
}
},
computed: {
formItems() {
const self = this
return [
{ {
type: 'input', type: 'input',
key: 'roleName', key: 'roleName',
label: '角色名', label: '角色名',
isSearch: true, isSearch: true,
placeholder: '请输入角色名', placeholder: '请输入角色名',
rules: [
{required: true, message: '请输入角色名'}
]
}, },
{ {
dataIndex: 'roleDescription', type: 'input',
key: 'roleDescription', key: 'roleDescription',
title: '角色描述' label: '角色描述'
},
{
type: 'treeSelect',
key: 'menuIds',
label: '菜单',
placeholder: '',
children: self.menus,
props: {
treeCheckable: true,
replaceFields: {
title: 'name',
value: 'id',
key: 'id',
children: 'children'
} }
],
apiConf: {
listApi: '/api/Role/all',
deleteApi: '/api/Role/remove',
editApi: '/api/Role/update',
addApi: '/api/Role/update',
} }
} }
]
}
},
created() {
this.getMenus()
},
methods: {
getMenus() {
this.$get('/api/Menu/list').then(({data}) => {
this.menus = data
})
}
} }
} }
</script> </script>

View File

@ -6,6 +6,7 @@
:columns="columns" :columns="columns"
:api-conf="apiConf" :api-conf="apiConf"
:form-items="formItems" :form-items="formItems"
@edit="editHandler"
/> />
</div> </div>
</template> </template>
@ -20,6 +21,8 @@ export default {
}, },
data () { data () {
return { return {
isEdit: false,
roles: [],
columns: [ columns: [
{ {
dataIndex: 'username', dataIndex: 'username',
@ -43,19 +46,38 @@ export default {
title: '角色' title: '角色'
} }
], ],
formItems: [ apiConf: {
listApi: { api: '/api/User/List', method: 'get' },
deleteApi: { api: '/api/User/DeleteUser', method: 'post', paramsType: 'Object', deleteKey: 'userId' } ,
editApi: { api: '/api/User/UpdateUser', method: 'post', },
addApi: { api: '/api/User/Add', method: 'post' }
}
}
},
computed: {
formItems () {
const self = this
return [
{ {
type: 'input', type: 'input',
key: 'username', key: 'username',
label: '用户名', label: '用户名',
isSearch: true, isSearch: true,
placeholder: '请输入用户名', placeholder: '请输入用户名',
rules: [
{ required: true, message: '请输入用户名' }
]
}, },
{ {
type: 'input', type: 'input',
key: 'password', key: 'password',
label: '密码', label: '密码',
placeholder: '请输入密码' placeholder: '请输入密码',
hide: self.isEdit,
rules: [
{ required: true, message: '请输入密码' }
]
}, },
{ {
type: 'input', type: 'input',
@ -68,14 +90,35 @@ export default {
key: 'phone', key: 'phone',
label: '电话', label: '电话',
placeholder: '请输入电话', placeholder: '请输入电话',
},
{
type: 'select',
key: 'roleId',
label: '角色',
placeholder: '请选择角色',
children: self.roles,
rules: [
{ required: true, message: '请选择角色' }
]
} }
], ]
apiConf: {
listApi: '/api/User/List',
deleteApi: '/api/User/DeleteUser',
editApi: '/api/User/UpdateUser',
addApi: '/api/User/Add',
} }
},
created() {
this.getRoles()
},
methods: {
editHandler (isEdit) {
this.isEdit = isEdit
},
getRoles () {
this.$get('/api/Role/all').then(({ data }) => {
this.roles = data.map(item => ({
label: item.roleName,
value: item.id
}))
})
} }
} }
} }

View File

@ -38,17 +38,35 @@ service.interceptors.response.use(
) )
Vue.prototype.$http = service Vue.prototype.$http = service
Vue.prototype.$post = (url, data) => { Vue.prototype.$post = (url, data, props = {}) => {
return service({ return service({
url, url,
method: "post", method: "post",
data data,
...props
}) })
} }
Vue.prototype.$get = (url, data) => { Vue.prototype.$put = (url, data, props = {}) => {
return service({
url,
method: "put",
data,
...props
})
}
Vue.prototype.$delete = (url, data, props = {}) => {
return service({
url,
method: "delete",
data,
...props
})
}
Vue.prototype.$get = (url, data, props = {}) => {
return service({ return service({
url, url,
method: "get", method: "get",
params: data params: data,
...props
}) })
} }