现有采购流程1
现有采购流程2
存在的问题,采购员可以不通过库房管理员直接完成入库,正常流程是,办理入库时,需要库房管理员确认。
正常合理的入库流程1
正常合理的入库流程2
数据库t_menu表有路径和组件的对应关系:
第一列时组件显示名称,第二列是访问地址(相对路径),第三列是组件vue文件的存放路径。
采购组件的结构(VUE)
- src
- views
- admin
- rurchase
- Rurchase.vue
- RurchaseAdd.vue(Rurchase.vue引用的子组件,应该可以做成公共组件)
有一个取巧的办法,把采购申请改成入库申请,交换采购和库管的权限,这样采购流程就可变为正常的入库流程,在vue前端修改页面显示内容即可。
src\views\admin\rurchase\Rurchase.vue:
- rurchase
- admin
- views
<template>
<a-card :bordered="false" class="card-area">
<div :class="advanced ? 'search' : null">
<!-- 搜索区域 -->
<a-form layout="horizontal">
<a-row :gutter="15">
<div :class="advanced ? null: 'fold'">
<a-col :md="6" :sm="24">
<a-form-item
label="单号"
:labelCol="{span: 4}"
:wrapperCol="{span: 18, offset: 2}">
<a-input v-model="queryParams.num"/>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item
label="申请人"
:labelCol="{span: 4}"
:wrapperCol="{span: 18, offset: 2}">
<a-input v-model="queryParams.applicant"/>
</a-form-item>
</a-col>
<a-col :md="6" :sm="24">
<a-form-item
label="入库状态"
:labelCol="{span: 4}"
:wrapperCol="{span: 18, offset: 2}">
<a-select v-model="queryParams.step" allowClear>
<a-select-option value="0">申请入库</a-select-option>
<a-select-option value="1">入库完成</a-select-option>
</a-select>
</a-form-item>
</a-col>
</div>
<span style="float: right; margin-top: 3px;">
<a-button type="primary" @click="search">查询</a-button>
<a-button style="margin-left: 8px" @click="reset">重置</a-button>
</span>
</a-row>
</a-form>
</div>
<div>
<div class="operator">
<a-button type="primary" ghost @click="add">新增</a-button>
<!-- <a-button @click="batchDelete">删除</a-button> 改为入库申请,不可删除入库记录 -->
</div>
<!-- 表格区域 -->
<a-table ref="TableInfo"
:columns="columns"
:rowKey="record => record.id"
:dataSource="dataSource"
:pagination="pagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
:scroll="{ x: 900 }"
@change="handleTableChange">
<template slot="numShow" slot-scope="text, record">
<template>
<a-badge v-if="record.step == 0" status="processing"/>
<a-badge v-if="record.step == 1" status="success"/>
{{ record.num }}
</template>
</template>
<template slot="operation" slot-scope="text, record">
<a-icon type="reconciliation" @click="view(record)" title="查 看" style="margin-right: 15px"></a-icon>
<a-icon type="download" @click="downLoad(record)" title="下 载" style="margin-right: 15px"></a-icon>
</template>
</a-table>
</div>
<rurchase-add
v-if="rurchaseAdd.visiable"
@close="handleRurchaseAddClose"
@success="handleRurchaseAddSuccess"
:rurchaseAddVisiable="rurchaseAdd.visiable">
</rurchase-add>
<rurchase-view
@close="handleRurchaseViewClose"
:rurchaseShow="rurchaseView.visiable"
:rurchaseData="rurchaseView.data">
</rurchase-view>
</a-card>
</template>
<script>
import RangeDate from '@/components/datetime/RangeDate'
import {mapState} from 'vuex'
import moment from 'moment'
import RurchaseAdd from './RurchaseAdd'
import RurchaseView from './RurchaseView'
import { newSpread, floatForm, floatReset, saveExcel } from '@/utils/spreadJS'
moment.locale('zh-cn')
export default {
name: 'Rurchase',
components: {RurchaseView, RurchaseAdd, RangeDate},
data () {
return {
rurchaseAdd: {
visiable: false
},
rurchaseEdit: {
visiable: false
},
rurchaseView: {
visiable: false,
data: null
},
advanced: false,
queryParams: {},
filteredInfo: null,
sortedInfo: null,
paginationInfo: null,
dataSource: [],
selectedRowKeys: [],
loading: false,
pagination: {
pageSizeOptions: ['10', '20', '30', '40', '100'],
defaultCurrent: 1,
defaultPageSize: 10,
showQuickJumper: true,
showSizeChanger: true,
showTotal: (total, range) => `显示 ${range[0]} ~ ${range[1]} 条记录,共 ${total} 条记录`
},
userList: [],
fileList: [],
previewVisible: false,
previewImage: ''
}
},
computed: {
...mapState({
currentUser: state => state.account.user
}),
columns () {
return [{
title: '采购单号',
dataIndex: 'num',
scopedSlots: {customRender: 'numShow'}
}, {
title: '申请人',
dataIndex: 'applicant'
}, {
title: '备 注',
dataIndex: 'content'
}, {
title: '预计价格',
dataIndex: 'price',
customRender: (text, row, index) => {
if (text !== null) {
return text + ' 元'
} else {
return '- -'
}
}
}, {
title: '当前流程',
dataIndex: 'step',
customRender: (text, row, index) => {
switch (text) {
case 0:
return <a-tag color="blue">申请入库</a-tag>
case 1:
return <a-tag color="green">入库完成</a-tag>
default:
return '- -'
}
}
}, {
title: '申请时间',
dataIndex: 'createDate',
customRender: (text, row, index) => {
if (text !== null) {
return text
} else {
return '- -'
}
}
}, {
title: '操作',
dataIndex: 'operation',
scopedSlots: {customRender: 'operation'}
}]
}
},
mounted () {
this.fetch()
},
methods: {
downLoad (row) {
this.$message.loading('正在生成', 0)
this.$get('/cos/goods-belong/getGoodsByNum', { num: row.num }).then((r) => {
let newData = []
r.data.data.forEach((item, index) => {
newData.push([(index + 1).toFixed(0), item.name, item.type !== null ? item.type : '- -', item.unit !== null ? item.unit : '- -', item.amount, item.price, ''])
})
let spread = newSpread('purchasePlan')
spread = floatForm(spread, 'purchasePlan', newData)
saveExcel(spread, '采购计划单.xlsx')
floatReset(spread, 'purchasePlan', newData.length)
this.$message.destroy()
})
},
add () {
this.rurchaseAdd.visiable = true
},
handleRurchaseAddClose () {
this.rurchaseAdd.visiable = false
},
handleRurchaseAddSuccess () {
this.rurchaseAdd.visiable = false
this.$message.success('入库申请已提交')
this.search()
},
edit (record) {
this.$refs.rurchaseEdit.setFormValues(record)
this.rurchaseEdit.visiable = true
},
handleRurchaseEditClose () {
this.rurchaseEdit.visiable = false
},
handleRurchaseEditSuccess () {
this.rurchaseEdit.visiable = false
this.$message.success('修改采购申请成功')
this.search()
},
view (row) {
this.rurchaseView.data = row
this.rurchaseView.visiable = true
},
handleRurchaseViewClose () {
this.rurchaseView.visiable = false
},
onSelectChange (selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys
},
toggleAdvanced () {
this.advanced = !this.advanced
},
handleDeptChange (value) {
this.queryParams.deptId = value || ''
},
/* 改为入库申请,禁用删除功能
batchDelete () {
if (!this.selectedRowKeys.length) {
this.$message.warning('请选择需要删除的记录')
return
}
let that = this
this.$confirm({
title: '确定删除所选中的记录?',
content: '当您点击确定按钮后,这些记录将会被彻底删除',
centered: true,
onOk () {
let ids = that.selectedRowKeys.join(',')
that.$delete('/cos/rurchase-request/' + ids).then(() => {
that.$message.success('删除成功')
that.selectedRowKeys = []
that.search()
})
},
onCancel () {
that.selectedRowKeys = []
}
})
}, */
search () {
let {sortedInfo, filteredInfo} = this
let sortField, sortOrder
// 获取当前列的排序和列的过滤规则
if (sortedInfo) {
sortField = sortedInfo.field
sortOrder = sortedInfo.order
}
this.fetch({
sortField: sortField,
sortOrder: sortOrder,
...this.queryParams,
...filteredInfo
})
},
reset () {
// 取消选中
this.selectedRowKeys = []
// 重置分页
this.$refs.TableInfo.pagination.current = this.pagination.defaultCurrent
if (this.paginationInfo) {
this.paginationInfo.current = this.pagination.defaultCurrent
this.paginationInfo.pageSize = this.pagination.defaultPageSize
}
// 重置列过滤器规则
this.filteredInfo = null
// 重置列排序规则
this.sortedInfo = null
// 重置查询参数
this.queryParams = {}
this.fetch()
},
handleTableChange (pagination, filters, sorter) {
// 将这三个参数赋值给Vue data,用于后续使用
this.paginationInfo = pagination
this.filteredInfo = filters
this.sortedInfo = sorter
this.fetch({
sortField: sorter.field,
sortOrder: sorter.order,
...this.queryParams,
...filters
})
},
fetch (params = {}) {
// 显示loading
this.loading = true
if (this.paginationInfo) {
// 如果分页信息不为空,则设置表格当前第几页,每页条数,并设置查询分页参数
this.$refs.TableInfo.pagination.current = this.paginationInfo.current
this.$refs.TableInfo.pagination.pageSize = this.paginationInfo.pageSize
params.size = this.paginationInfo.pageSize
params.current = this.paginationInfo.current
} else {
// 如果分页信息为空,则设置为默认值
params.size = this.pagination.defaultPageSize
params.current = this.pagination.defaultCurrent
}
if (params.step === undefined) {
delete params.step
}
this.$get('/cos/rurchase-request/page', {
...params
}).then((r) => {
let data = r.data.data
const pagination = {...this.pagination}
pagination.total = data.total
this.dataSource = data.records
this.pagination = pagination
// 数据加载完毕,关闭loading
this.loading = false
})
}
},
watch: {}
}
</script>
<style lang="less" scoped>
@import "../../../../static/less/Common";
</style>
src\views\admin\rurchase\RurchaseAdd.vue:
<template>
<a-drawer
title="新增入库申请"
:maskClosable="false"
placement="right"
:closable="false"
:visible="show"
:width="1200"
@close="onClose"
style="height: calc(100% - 55px);overflow: auto;padding-bottom: 53px;"
>
<a-form :form="form" layout="vertical">
<a-row :gutter="20">
<a-col :span="12">
<a-form-item label='申请人' v-bind="formItemLayout">
<a-input v-decorator="[
'applicant',
{ rules: [{ required: true, message: '请输入申请人!' }] }
]"/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label='备注消息' v-bind="formItemLayout">
<a-textarea :rows="4" v-decorator="[
'content',
{ rules: [{ required: true, message: '请输入备注消息!' }] }
]"/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-table :columns="columns" :data-source="dataList">
<template slot="nameShow" slot-scope="text, record">
<a-input v-model="record.name"></a-input>
</template>
<template slot="typeShow" slot-scope="text, record">
<a-input v-model="record.type"></a-input>
</template>
<template slot="typeIdShow" slot-scope="text, record">
<a-select v-model="record.typeId" style="width: 100%">
<a-select-option v-for="(item, index) in consumableType" :value="item.id" :key="index">{{ item.name }}</a-select-option>
</a-select>
</template>
<template slot="unitShow" slot-scope="text, record">
<a-input v-model="record.unit"></a-input>
</template>
<template slot="amountShow" slot-scope="text, record">
<a-input-number v-model="record.amount" :min="1" :step="1"/>
</template>
<template slot="priceShow" slot-scope="text, record">
<a-input-number v-model="record.price" :min="1"/>
</template>
</a-table>
<a-button @click="dataAdd" type="primary" ghost size="large" style="margin-top: 10px;width: 100%">
新增物品
</a-button>
</a-col>
</a-row>
</a-form>
<div class="drawer-bootom-button">
<a-popconfirm title="确定放弃编辑?" @confirm="onClose" okText="确定" cancelText="取消">
<a-button style="margin-right: .8rem">取消</a-button>
</a-popconfirm>
<a-button @click="handleSubmit" type="primary" :loading="loading">提交</a-button>
</div>
</a-drawer>
</template>
<script>
import {mapState} from 'vuex'
const formItemLayout = {
labelCol: { span: 24 },
wrapperCol: { span: 24 }
}
export default {
name: 'RurchaseAdd',
props: {
rurchaseAddVisiable: {
default: false
}
},
computed: {
...mapState({
currentUser: state => state.account.user
}),
show: {
get: function () {
return this.rurchaseAddVisiable
},
set: function () {
}
},
columns () {
return [{
title: '物品名称',
dataIndex: 'name',
scopedSlots: {customRender: 'nameShow'}
}, {
title: '型号',
dataIndex: 'type',
scopedSlots: {customRender: 'typeShow'}
}, {
title: '数量',
dataIndex: 'amount',
scopedSlots: {customRender: 'amountShow'}
}, {
title: '所属类型',
dataIndex: 'typeId',
width: 200,
scopedSlots: {customRender: 'typeIdShow'}
}, {
title: '单位',
dataIndex: 'unit',
scopedSlots: {customRender: 'unitShow'}
}, {
title: '单价',
dataIndex: 'price',
scopedSlots: {customRender: 'priceShow'}
}]
}
},
data () {
return {
formItemLayout,
form: this.$form.createForm(this),
loading: false,
dataList: [],
consumableType: []
}
},
mounted () {
this.getConsumableType()
},
methods: {
getConsumableType () {
this.$get('/cos/consumable-type/list').then((r) => {
this.consumableType = r.data.data
})
},
dataAdd () {
this.dataList.push({name: '', type: '', typeId: '', unit: '', amount: '', price: ''})
},
reset () {
this.loading = false
this.form.resetFields()
},
onClose () {
this.reset()
this.$emit('close')
},
handleSubmit () {
if (this.dataList.length !== 0) {
let price = 0
this.dataList.forEach(item => {
price += item.price * item.amount
})
this.form.validateFields((err, values) => {
if (!err) {
values.price = price
values.goods = JSON.stringify(this.dataList)
this.loading = true
this.$post('/cos/rurchase-request', {
...values
}).then((r) => {
this.reset()
this.$emit('success')
}).catch(() => {
this.loading = false
})
}
})
} else {
this.$message.warning('请添加记录!')
}
}
}
}
</script>
<style scoped>
</style>
<template>
<a-modal v-model="show" title="入库申请信息" @cancel="onClose" :width="800">
<template slot="footer">
<a-button key="back" @click="onClose" type="danger">
关闭
</a-button>
</template>
<div style="font-size: 13px" v-if="rurchaseData !== null">
<div style="padding-left: 24px;padding-right: 24px;margin-bottom: 50px;margin-top: 50px">
<a-steps :current="current" progress-dot size="small">
<a-step title="已提交" />
<a-step title="入库中" />
<a-step title="入库完成" />
</a-steps>
</div>
<a-row style="padding-left: 24px;padding-right: 24px;">
<a-col style="margin-bottom: 15px"><span style="font-size: 15px;font-weight: 650;color: #000c17">基础信息</span></a-col>
<a-col :span="8"><b>采购单:</b>
{{ rurchaseData.num }}
</a-col>
<a-col :span="8"><b>预计价格:</b>
{{ rurchaseData.price }} 元
</a-col>
<a-col :span="8"><b>申请人:</b>
{{ rurchaseData.applicant }}
</a-col>
</a-row>
<br/>
<a-row style="padding-left: 24px;padding-right: 24px;">
<a-col :span="8"><b>当前状态:</b>
<span v-if="rurchaseData.step == 0">入库中</span>
<span v-if="rurchaseData.step == 1">入库完成</span>
</a-col>
<a-col :span="8"><b>备注信息:</b>
{{ rurchaseData.content }}
</a-col>
<a-col :span="8"><b>申请时间:</b>
{{ rurchaseData.createDate }}
</a-col>
</a-row>
<br/>
<br/>
<a-row style="padding-left: 24px;padding-right: 24px;" :gutter="15">
<a-col style="margin-bottom: 15px"><span style="font-size: 15px;font-weight: 650;color: #000c17">物品详情</span></a-col>
<a-col :span="24">
<a-table :columns="columns" :data-source="goodsList">
</a-table>
</a-col>
</a-row>
</div>
</a-modal>
</template>
<script>
import moment from 'moment'
moment.locale('zh-cn')
export default {
name: 'RurchaseView',
props: {
rurchaseShow: {
type: Boolean,
default: false
},
rurchaseData: {
type: Object
}
},
computed: {
show: {
get: function () {
return this.rurchaseShow
},
set: function () {
}
},
columns () {
return [{
title: '物品名称',
dataIndex: 'name'
}, {
title: '型号',
dataIndex: 'type'
}, {
title: '数量',
dataIndex: 'amount'
}, {
title: '所属类型',
dataIndex: 'consumableName'
}, {
title: '单位',
dataIndex: 'unit'
}, {
title: '单价',
dataIndex: 'price'
}]
}
},
data () {
return {
loading: false,
goodsList: [],
current: 0
}
},
watch: {
rurchaseShow: function (value) {
if (value) {
if (this.rurchaseData.step === 0) {
this.current = 1
}
if (this.rurchaseData.step === 1) {
this.current = 2
}
this.getGoodsByNum(this.rurchaseData.num)
}
}
},
methods: {
getGoodsByNum (num) {
this.$get('/cos/goods-belong/getGoodsByNum', { num }).then((r) => {
this.goodsList = r.data.data
console.log(this.goodsList)
})
},
onClose () {
this.$emit('close')
}
}
}
</script>
<style scoped>
</style>
上述修改不涉及js代码和逻辑,完全是显示内容的修改。
修改后可以通过系统管理员账号,修改菜单的层级和角色权限:
总结
前端系统的路径,之所以做到数据库t_menu表中,就是因为好进行后台管理,系统管理员可以定制每个人能查看的页面,以及页面的层级归属。
整个系统有许多功能是为测试而写的,没有实用价值,需要梳理后进行清理。