在vue页面中添加组件到底有多方便

修改vue写的前端页面到底有多方便?如果熟练的话,出乎你想象的快。

原来的页面:/admin/stock

原来的文件地址:src\views\admin\stock\Stock.vue

另一个页面有个入库功能,需要转移到上面的页面中:

路径:/purchase/request

地址:src\views\purchase\request\Request.vue

入库功能包括一个开启按键(1)和一个弹出表单(2):

 

开启按键代码:

<a-button type="primary" ghost @click="warehouse">入库</a-button>
    warehouse () {
      this.stockAdd.visiable = true
    },

弹出表单窗口是一个独立存在的vue文件:

路径:src\views\purchase\request\RequestAdd.vue

RequestAdd.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="horizontal">
      <a-row :gutter="50">
        <a-col :span="12">
          <a-form-item label='保管人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'custodian',
            { rules: [{ required: true, message: '请输入保管人!' }] }
            ]"/>
          </a-form-item>
        </a-col>
        <a-col :span="12">
          <a-form-item label='入库人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'putUser',
            { 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: 'requestAdd',
  props: {
    requestAddVisiable: {
      default: false
    }
  },
  computed: {
    ...mapState({
      currentUser: state => state.account.user
    }),
    show: {
      get: function () {
        return this.requestAddVisiable
      },
      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'}
      }]
    }
  },
  mounted () {
    this.getConsumableType()
  },
  data () {
    return {
      dataList: [],
      formItemLayout,
      form: this.$form.createForm(this),
      loading: false,
      consumableType: []
    }
  },
  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 () {
      let price = 0
      this.dataList.forEach(item => {
        price += item.price * item.amount
      })
      this.form.validateFields((err, values) => {
        values.price = price
        values.goods = JSON.stringify(this.dataList)
        if (!err) {
          this.loading = true
          this.$post('/cos/stock-info/put', {
            ...values
          }).then((r) => {
            this.reset()
            this.$emit('success')
          }).catch(() => {
            this.loading = false
          })
        }
      })
    }
  }
}
</script>

<style scoped>

</style>

在Request.vue中引用RequestAdd.vue的方法:

    <request-add
      v-if="requestAdd.visiable"
      @close="handleRequestAddClose"
      @success="handleRequestAddSuccess"
      :requestAddVisiable="requestAdd.visiable">
    </request-add>
<script>
...
import RequestAdd from './RequestAdd'
...

export default {
  ...
  data () {
    return {
      ...
      requestAdd: {
        visiable: false
      },
      ...
    }
  },
  ...
  methods: {
    ...
    handleRequestAddClose () {
      this.requestAdd.visiable = false
    },
    handleRequestAddSuccess () {
      this.requestAdd.visiable = false
      this.$message.success('入库成功')
      this.search()
    },
    ...
    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
      })
    },
    ...
    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.typeId === undefined) {
        delete params.typeId
      }
      this.$get('/cos/stock-info/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>

实际上就是在Request.vue中添加一个数据对象(requestAdd.visiable)和两个方法(handleRequestAddClose、handleRequestAddSuccess)

开始改造

将RequestAdd.vue文件加入Stock.vue所在路径,修改合适的文件名称为StockAdd.vue,同时修改StockAdd.vue对外暴露的name和props的名称。

在Stock.vue的<template>代码适当位置添加按键和表单(7行代码):

 在Stock.vue的<script>代码中添加相关的引用(2行代码)、数据(3行代码)、函数(8行代码):

 

总结

改造过程,共复制vue文件1个,修改代码23行。熟悉代码的情况下,修改用时很短。

说明vue组件化特性为代码复用带来极大便利。

源码

src\views\purchase\request\Request.vue

<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.name"/>
              </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.type"/>
              </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.typeId" style="width: 100%" allowClear>
                  <a-select-option v-for="(item, index) in consumableType" :value="item.id" :key="index">{{ item.name }}</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> 后台API不存在/cos/request-type/{id}删除接口 这个按键是多余的 -->
      </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="titleShow" slot-scope="text, record">
          <template>
            <a-badge status="processing"/>
            <a-tooltip>
              <template slot="title">
                {{ record.title }}
              </template>
              {{ record.title.slice(0, 8) }} ...
            </a-tooltip>
          </template>
        </template>
        <template slot="contentShow" slot-scope="text, record">
          <template>
            <a-tooltip>
              <template slot="title">
                {{ record.content }}
              </template>
              {{ record.content.slice(0, 30) }} ...
            </a-tooltip>
          </template>
        </template>
        <template slot="operation" slot-scope="text, record">
          <a-icon type="setting" theme="twoTone" twoToneColor="#4a9ff5" @click="edit(record)" title="修 改"></a-icon>
        </template>
      </a-table>
    </div>
    <request-add
      v-if="requestAdd.visiable"
      @close="handleRequestAddClose"
      @success="handleRequestAddSuccess"
      :requestAddVisiable="requestAdd.visiable">
    </request-add>
  </a-card>
</template>

<script>
import RangeDate from '@/components/datetime/RangeDate'
import RequestAdd from './RequestAdd'
import {mapState} from 'vuex'
import moment from 'moment'
moment.locale('zh-cn')

export default {
  name: 'request',
  components: {RequestAdd, RangeDate},
  data () {
    return {
      advanced: false,
      requestAdd: {
        visiable: false
      },
      requestEdit: {
        visiable: 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} 条记录`
      },
      consumableType: []
    }
  },
  computed: {
    ...mapState({
      currentUser: state => state.account.user
    }),
    columns () {
      return [{
        title: '物品名称',
        dataIndex: 'name'
      }, {
        title: '型号',
        dataIndex: 'type',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '物品数量',
        dataIndex: 'amount',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '单位',
        dataIndex: 'unit',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '单价',
        dataIndex: 'price',
        customRender: (text, row, index) => {
          if (text !== null) {
            return '¥' + text.toFixed(2)
          } else {
            return '- -'
          }
        }
      }, {
        title: '总价',
        dataIndex: 'allPrice',
        customRender: (text, row, index) => {
          return '¥' + (row.price * row.amount).toFixed(2)
        }
      }, {
        title: '物品类型',
        dataIndex: 'consumableType',
        customRender: (text, row, index) => {
          if (text !== null) {
            return <a-tag>{text}</a-tag>
          } else {
            return '- -'
          }
        }
      }, {
        title: '备注',
        dataIndex: 'content',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '入库时间',
        dataIndex: 'createDate',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }]
    }
  },
  mounted () {
    this.fetch()
    this.getConsumableType()
  },
  methods: {
    getConsumableType () {
      this.$get('/cos/consumable-type/list').then((r) => {
        this.consumableType = r.data.data
      })
    },
    onSelectChange (selectedRowKeys) {
      this.selectedRowKeys = selectedRowKeys
    },
    toggleAdvanced () {
      this.advanced = !this.advanced
    },
    add () {
      this.requestAdd.visiable = true
    },
    handleRequestAddClose () {
      this.requestAdd.visiable = false
    },
    handleRequestAddSuccess () {
      this.requestAdd.visiable = false
      this.$message.success('入库成功')
      this.search()
    },
    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/request-type/' + 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.typeId === undefined) {
        delete params.typeId
      }
      this.$get('/cos/stock-info/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\purchase\request\RequestAdd.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="horizontal">
      <a-row :gutter="50">
        <a-col :span="12">
          <a-form-item label='保管人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'custodian',
            { rules: [{ required: true, message: '请输入保管人!' }] }
            ]"/>
          </a-form-item>
        </a-col>
        <a-col :span="12">
          <a-form-item label='入库人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'putUser',
            { 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: 'requestAdd',
  props: {
    requestAddVisiable: {
      default: false
    }
  },
  computed: {
    ...mapState({
      currentUser: state => state.account.user
    }),
    show: {
      get: function () {
        return this.requestAddVisiable
      },
      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'}
      }]
    }
  },
  mounted () {
    this.getConsumableType()
  },
  data () {
    return {
      dataList: [],
      formItemLayout,
      form: this.$form.createForm(this),
      loading: false,
      consumableType: []
    }
  },
  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 () {
      let price = 0
      this.dataList.forEach(item => {
        price += item.price * item.amount
      })
      this.form.validateFields((err, values) => {
        values.price = price
        values.goods = JSON.stringify(this.dataList)
        if (!err) {
          this.loading = true
          this.$post('/cos/stock-info/put', {
            ...values
          }).then((r) => {
            this.reset()
            this.$emit('success')
          }).catch(() => {
            this.loading = false
          })
        }
      })
    }
  }
}
</script>

<style scoped>

</style>

 src\views\admin\stock\Stock.vue(修改后):

<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.name"/>
              </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.type"/>
              </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.typeId" style="width: 100%" allowClear>
                  <a-select-option v-for="(item, index) in consumableType" :value="item.id" :key="index">{{ item.name }}</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="warehouse">入库</a-button>
        <a-button type="primary" ghost @click="outOfWarehouse">出库</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="titleShow" slot-scope="text, record">
          <template>
            <a-badge status="processing"/>
            <a-tooltip>
              <template slot="title">
                {{ record.title }}
              </template>
              {{ record.title.slice(0, 8) }} ...
            </a-tooltip>
          </template>
        </template>
        <template slot="contentShow" slot-scope="text, record">
          <template>
            <a-tooltip>
              <template slot="title">
                {{ record.content }}
              </template>
              {{ record.content.slice(0, 30) }} ...
            </a-tooltip>
          </template>
        </template>
        <template slot="operation" slot-scope="text, record">
          <a-icon type="setting" theme="twoTone" twoToneColor="#4a9ff5" @click="edit(record)" title="修 改"></a-icon>
        </template>
      </a-table>
    </div>
    <stock-add
      v-if="stockAdd.visiable"
      @close="handleStockAddClose"
      @success="handleStockAddSuccess"
      :stockAddVisiable="stockAdd.visiable">
    </stock-add>
    <stock-out
      @close="handleStockoutClose"
      @success="handleStockoutSuccess"
      :stockoutData="stockout.data"
      :stockoutVisiable="stockout.visiable">
    </stock-out>
  </a-card>
</template>

<script>
import RangeDate from '@/components/datetime/RangeDate'
import {mapState} from 'vuex'
import StockOut from './StockOut'
import StockAdd from './StockAdd'
import moment from 'moment'
moment.locale('zh-cn')

export default {
  name: 'Stock',
  components: {StockOut, StockAdd, RangeDate},
  data () {
    return {
      advanced: false,
      stockAdd: {
        visiable: false
      },
      stockout: {
        visiable: false,
        data: null
      },
      requestEdit: {
        visiable: false
      },
      queryParams: {},
      filteredInfo: null,
      sortedInfo: null,
      paginationInfo: null,
      dataSource: [],
      selectedRowKeys: [],
      selectedRows: [],
      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} 条记录`
      },
      consumableType: []
    }
  },
  computed: {
    ...mapState({
      currentUser: state => state.account.user
    }),
    columns () {
      return [{
        title: '物品名称',
        dataIndex: 'name'
      }, {
        title: '型号',
        dataIndex: 'type',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '物品数量',
        dataIndex: 'amount',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '单位',
        dataIndex: 'unit',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '单价',
        dataIndex: 'price',
        customRender: (text, row, index) => {
          if (text !== null) {
            return '¥' + text.toFixed(2)
          } else {
            return '- -'
          }
        }
      }, {
        title: '总价',
        dataIndex: 'allPrice',
        customRender: (text, row, index) => {
          return '¥' + (row.price * row.amount).toFixed(2)
        }
      }, {
        title: '物品类型',
        dataIndex: 'consumableType',
        customRender: (text, row, index) => {
          if (text !== null) {
            return <a-tag>{text}</a-tag>
          } else {
            return '- -'
          }
        }
      }, {
        title: '备注',
        dataIndex: 'content',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }, {
        title: '入库时间',
        dataIndex: 'createDate',
        customRender: (text, row, index) => {
          if (text !== null) {
            return text
          } else {
            return '- -'
          }
        }
      }]
    }
  },
  mounted () {
    this.fetch()
    this.getConsumableType()
  },
  methods: {
    getConsumableType () {
      this.$get('/cos/consumable-type/list').then((r) => {
        this.consumableType = r.data.data
      })
    },
    onSelectChange (selectedRowKeys, selectedRows) {
      selectedRows.forEach(item => {
        if (item.amount === 0) {
          this.$message.warning(`${item.name}没有库存!`)
          return false
        }
      })
      this.selectedRowKeys = selectedRowKeys
      this.selectedRows = selectedRows
    },
    toggleAdvanced () {
      this.advanced = !this.advanced
    },
    // “入库”按键响应
    warehouse () {
      this.stockAdd.visiable = true
    },
    // “出库”按键的响应方法
    outOfWarehouse () {
      if (!this.selectedRowKeys.length) {
        this.$message.warning('请选择需要出库的物品')
        return
      }
      let goods = this.selectedRows
      let amt = false
      let warningwords = '某个物品'
      goods.forEach(item => {
        item.max = item.amount
        if (item.amount === 0) {
          amt = true
          warningwords = item.name
        }
      })
      if (amt) {
        this.$message.warning(`${warningwords}没有库存!`)
        return
      }
      this.stockout.data = JSON.parse(JSON.stringify(goods))
      this.stockout.visiable = true
    },
    handleStockAddClose () {
      this.stockAdd.visiable = false
    },
    handleStockAddSuccess () {
      this.stockAdd.visiable = false
      this.$message.success('入库成功')
      this.search()
    },
    handleStockoutClose () {
      this.stockout.visiable = false
    },
    handleStockoutSuccess () {
      this.stockout.visiable = false
      this.selectedRows = []
      this.selectedRowKeys = []
      this.$message.success('出库成功')
      this.search()
    },
    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/request-type/' + ids).then(() => {
            that.$message.success('删除成功')
            that.selectedRowKeys = []
            that.selectedRows = []
            that.search()
          })
        },
        onCancel () {
          that.selectedRowKeys = []
          that.selectedRows = []
        }
      })
    },
    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.typeId === undefined) {
        delete params.typeId
      }
      this.$get('/cos/stock-info/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\stock\StockAdd.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="horizontal">
      <a-row :gutter="50">
        <a-col :span="12">
          <a-form-item label='保管人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'custodian',
            { rules: [{ required: true, message: '请输入保管人!' }] }
            ]"/>
          </a-form-item>
        </a-col>
        <a-col :span="12">
          <a-form-item label='入库人' v-bind="formItemLayout">
            <a-input v-decorator="[
            'putUser',
            { 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: 'stockAdd',
  props: {
    stockAddVisiable: {
      default: false
    }
  },
  computed: {
    ...mapState({
      currentUser: state => state.account.user
    }),
    show: {
      get: function () {
        return this.stockAddVisiable
      },
      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'}
      }]
    }
  },
  mounted () {
    this.getConsumableType()
  },
  data () {
    return {
      dataList: [],
      formItemLayout,
      form: this.$form.createForm(this),
      loading: false,
      consumableType: []
    }
  },
  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 () {
      let price = 0
      this.dataList.forEach(item => {
        price += item.price * item.amount
      })
      this.form.validateFields((err, values) => {
        values.price = price
        values.goods = JSON.stringify(this.dataList)
        if (!err) {
          this.loading = true
          this.$post('/cos/stock-info/put', {
            ...values
          }).then((r) => {
            this.reset()
            this.$emit('success')
          }).catch(() => {
            this.loading = false
          })
        }
      })
    }
  }
}
</script>

<style scoped>

</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/191982.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MindStudio学习记录三:推理应用开发 acl mindx sdk

1.推理应用流程 1.1.创建工程 1.2.模型转换 1.3代码开发 1.3.1ACL代码 1.3.2MindX SDK开发 可视化模块化设计 中间的图片与处理 是基于AIPP的可视化处理 1.5.编译 交叉编译 1.6.运行与调试 1.7 调优工具 profiling性能分析 2.开发举例 resnet-50 2.1 准备工程 2.2.准备模型…

C#-基础及扩展合集(持续更新)

一、基础 Ⅰ 关键字 1、record record&#xff08;记录&#xff09;&#xff0c;编译器会在后台创建一个类。支持类似于结构的值定义&#xff0c;但被实现为一个类&#xff0c;方便创建不可变类型&#xff0c;成员在初始化后不能再被改变 &#xff08;C#9新增&#xff09; …

MT6893_天玑 1200芯片规格参数介绍_datasheet规格书

天玑 1200(MT6893)是一款专为旗舰级全新5G芯片&#xff0c;它融合了先进的AI、相机和多媒体技术&#xff0c;为用户带来令人惊叹的体验。采用先进的6纳米制程设计&#xff0c;内置各种先进技术。该芯片采用旗舰级的八核CPU架构设计&#xff0c;支持16GB强大的四通道内存以及双通…

西门子PLC与组态王无线通讯中如何设置从站

本方案主要详述了在多台西门子300PLC与组态王之间Modbus无线通讯中如何设置从站。方案中所用到的无线通讯终端是DTD434MC——欧美系PLC专用无线通讯终端。 一、方案概述 无线Modbus网络组成如下&#xff1a; 二、测试背景 ● PC端组态软件版本&#xff1a;组态王6.55 ● 默…

CRM的智能招投标对企业有什么意义?

如今CRM系统的生态系统越来越壮大&#xff0c;这些工具的集成极大地丰富了CRM系统的应用场景&#xff0c;例如CRM系统集成企业微信等社交媒体为获客提供便利&#xff1b;再比如CRM集成ChatGPT提高邮件内容质量&#xff0c;对于经常接触招投标项目的业务人员来说&#xff0c;在C…

在NAS上部署.NET版本的WOL远程开机服务

在本文中&#xff0c;我们将以部署基于.NET的WOL远程开机服务为例&#xff0c;详细介绍如何利用Docker技术在群辉部署ASP.NET服务。同时&#xff0c;我们还将展示如何对原有的控制台WOL进行改造&#xff0c;以及如何使用SignAuthorization简易URL验签类库。文章相关的代码开源地…

在PostGIS中进行点数据的等值线提取

说明 介绍在PostGIS中从点数据提取等值线。 关键字&#xff1a; raster、point、PostGIS、等值线 环境准备 Postgresql版本&#xff1a;PostgreSQL 14.0, 64-bitPostGIS版本&#xff1a;POSTGIS"3.3.2"QGIS版本&#xff1a;3.28.3-Firenze&#xff08;验证用&…

Windows系统下更新后自带的画图软件出现马赛克bug

一.bug的样子&#x1f357; 在使用橡皮后&#xff0c;原来写的内容会变成马赛克。而我们希望它是纯白色的。 二.解决方法&#x1f357; 第一步 第二步 第三步 三. 解决后的效果&#x1f357; 用橡皮擦随便擦都不会出现马赛克了。 更新过后&#xff0c;想用win自带的画图软件会出…

免杀原理(php)

免杀原理 0x01 前言 何为免杀&#xff0c;免杀就是一种逃脱杀毒软件查杀的方法&#xff0c;免杀的目的就是绕过“墙”&#xff0c;去执行危险的操作。那么如何绕过这堵“墙”&#xff0c;就是免杀的本质。有句俗话说得好“知己知彼&#xff0c;百战不殆”&#xff0c;想要用好…

VM安装Centos

文章目录 第2章 VM与Linux的安装2.1 VMWare安装2.2 CentOS安装 第3章 Linux文件与目录结构3.1 Linux文件3.2 Linux目录结构 第4章 VI/VIM编辑器4.1 是什么4.2 测试数据准备4.3 一般模式4.4 编辑模式4.5 命令模式4.6 模式间转换 第5章 网络配置和系统管理操作5.1 查看网络IP和网…

sam和mobilesam的c++ dll打包

一、前言 前面我们已经成功导出分割模型和预处理模型了&#xff1a; 因此接下来就是要c推理的测试了 二、c推理

OpenCV入门11——图像的分割与修复

文章目录 图像分割的基本概念实战-分水岭法(一)实战-分水岭法(二)GrabCut基本原理实战-GrabCut主体程序的实现实战-GrabCut鼠标事件的处理实战-调用GrabCut实现图像分割meanshift图像分割视频前后景分离其它对视频前后影分离的方法图像修复 图像分割是计算机视觉中的一个重要领…

基于springboot实现智慧党建系统项目【项目源码】

基于springboot实现智慧党建系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#xff0c;所以…

cpp中虚实继承问题

1.一个基类base&#xff0c;被类a虚继承&#xff0c;类a被其他的类继续继承&#xff0c;那么base中的初始化必须由派生类的最后一个完成&#xff0c;其中任意一个都不能代替完成基类&#xff0c;如果在最后一个派生类里不进行这个base的初始化&#xff0c;那么就会调用相应的无…

全面探讨HTTP协议从0.9到3.0版本的发展和特点

前言&#xff1a; 最近的几场面试都问到了http的相关知识点&#xff0c;博主在此结合书籍和网上资料做下总结。本篇文章讲收录到秋招专题&#xff0c;该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮…

NetSuite 应用性能管理器(APM)

前段时间&#xff0c;我们发过一个文章谈系统健康检查。 NetSuite ERP系统健康检查-CSDN博客文章浏览阅读119次。“健康检查”本质上是属于信息化持续改善的组成部分。如果说信息化是一个持续不断的过程的话&#xff0c;那么“健康检查”就是持续不断的PDCA中的Check那一环。h…

Python实现定时任务的方案及其比较

目录 一、引言 二、基于time模块的定时任务 三、基于schedule库的定时任务 四、基于APScheduler库的定时任务 五、基于Celery库的定时任务 七、总结与比较 一、引言 在Python中&#xff0c;定时任务指的是在特定的时间间隔或者特定的时间点自动执行某些操作的任务。定时…

C语言--利用选择法对数组中的10个整数按由小到大排序

一.选择法排序介绍&#x1f357; 所谓选择法就是先将10个数中最小的数字与arr[0]交换&#xff0c;再将arr[1]-arr[9]中最小的数字与arr[1]进行交换....每一次比较&#xff0c;找出一个未经排序的数中最小的一个。总共比较9轮。 下面以5个数字为例说明选择法的步骤。 二.完整代码…

PubMedBERT:生物医学自然语言处理领域的特定预训练模型

今年大语言模型的快速发展导致像BERT这样的模型都可以称作“小”模型了。Kaggle LLM比赛LLM Science Exam 的第四名就只用了deberta&#xff0c;这可以说是一个非常好的成绩了。所以说在特定的领域或者需求中&#xff0c;大语言模型并不一定就是最优的解决方案&#xff0c;“小…

自动化横行时代,手工测试如何突破重围?测试之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试是每个…
最新文章