基础
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
例子:
// 定义一个混入对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个使用混入对象的组件
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
选项合并
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
var mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
值为对象的选项,例如 methods
、components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
注意:Vue.extend()
也使用同样的策略进行合并。
全局混入
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"
重点事项
混入的变量,方法都可当本地方法一样调用,非常方便,可以把同样的代码放入Minxins中复用
示例代码
@/view/mixins/common.js
import SearchForm from '@/view/public/table-form'; import screenfull from 'screenfull'; import { httpDelete, httpGetParams, httpPost } from '@api/data'; import axios from 'axios'; export default { components: { SearchForm }, data () { return { loading: false, loadingSubmit: false, columnsDisplay: [], datas: [], page: 1, pageSize: 40, total: 0, filterValue: {}, formModal: false, formParams: {}, editIndex: -1, formEdit: null, searchParams: {}, tableSize: 'default', tableFullscreen: false, tableHeight: 500 } }, computed: { // 动态设置列 tableColumns () { const columns = [...this.columns]; return columns.filter(item => item.show); }, searchValue: function () { let re = { pageSize: this.pageSize, page: this.page } return Object.assign(re, this.searchParams, this.filterValue) } }, methods: { rowClassName: function (row, index) { if (row.hidden) { return 'row-hidden' } return '' }, changeSearchParams (params) { if (params) { this.searchParams = params } else { this.searchParams = {} } this.page = 1 this.getData() }, getData () { let _this = this _this.datas = [] if (this.cancel) { this.cancel('cancel') } _this.loading = true httpGetParams(_this.getDataUrl, _this.searchValue, new axios.CancelToken(function executor (c) { _this.cancel = c })).then(res => { _this.datas = res.data _this.total = parseInt(res.total) _this.loading = false }).catch(() => { _this.loading = false // console.log(err) }) }, handleAdd: function () { this.editIndex = -1 this.formEdit = JSON.parse(JSON.stringify(this.formParams)) this.formModal = true }, handleEdit: function (row, index) { this.editIndex = index this.formEdit = JSON.parse(JSON.stringify(row)) this.formModal = true }, handleDel: function (row, index) { let _this = this if (_this.cancel) { _this.cancel('cancel') } _this.$Modal.confirm({ title: _this.$t('common.del_confirm_title'), content: _this.$t('common.del_confirm_content'), onOk: function () { httpDelete(_this.delUrl + '/' + row.id, null, new axios.CancelToken(function executor (c) { _this.cancel = c })) .then((data) => { _this.$Message.success(_this.$t('common.del_success')) _this.datas.splice(index, 1) }) .catch((error) => { if (error.message !== 'cancel') { } }) } }) }, transformFormInfo: function (formInfo) { return { ...formInfo } }, saveData: function (formInfo) { let _this = this if (this.cancel) { this.cancel('cancel') } let url = _this.editUrl if (this.editIndex > -1) { url = url + '/' + formInfo.id } _this.loadingSubmit = true httpPost(url, this.transformFormInfo(formInfo), new axios.CancelToken(function executor (c) { _this.cancel = c })).then(res => { _this.loadingSubmit = false _this.$Message.success(_this.$t('common.operate_success')) _this.formModal = false if (_this.editIndex === -1) { _this.datas.splice(0, 0, res) } else { _this.datas.splice(_this.editIndex, 1, res) } }).catch(() => { _this.loadingSubmit = false // console.log(err) }) }, // 表格全屏 handleFullscreen () { this.tableFullscreen = !this.tableFullscreen; if (this.tableFullscreen) { screenfull.request(this.$refs.card.$el); } else { screenfull.exit(); } }, // 改变表格尺寸 handleChangeTableSize (size) { this.tableSize = size; }, // 刷新表格数据 handleRefresh () { this.getData(); }, // 重置表格列设置 handleResetColumn () { this.columns = this.columns.map((item, i) => { const newItem = item; newItem.show = this.columnsDisplay[i]; return newItem; }); }, // 切换页码 pageChange: function (page) { this.page = page this.getData() }, // 切换每页条数 handleChangePageSize (size) { this.pageSize = size; this.getData(); }, // 改变表格高度 changeTableHeight: function () { this.$nextTick(() => { this.tableHeight = window.innerHeight - this.calOffsetTop(this.$refs.table.$el) - this.$refs.pageFooter.$el.offsetHeight - 16 }) }, calOffsetTop: function (el) { if (!el.offsetParent) { return el.offsetTop } else { return el.offsetTop + this.calOffsetTop(el.offsetParent) } } }, mounted () { this.changeTableHeight() window.onresize = () => { this.changeTableHeight() } }, created () { if (this.columns) { this.columnsDisplay = this.columns.map(_ => !!_.show) } } }
把增删改查放到一起,把表格高度控制放在一起,vue文件中的调用如下,这是一个防伪码批次管理
<template> <div> <Card :bordered="false" dis-hover class="list-table-list-card ivu-mt" ref="card"> <SearchForm ref="form" :search-params="searchOption" @on-submit="changeSearchParams" @on-reset="changeSearchParams" @on-collapse="changeTableHeight"/> <Button type="primary" icon="md-add" @click="handleAdd">{{ $t('common.add') }}</Button> <div class="ivu-inline-block ivu-fr"> <Dropdown @on-click="handleChangeTableSize" trigger="click"> <Tooltip class="ivu-ml" :content="$t('common.density')" placement="top"> <i-link> <Icon size="16" type="md-list"/> </i-link> </Tooltip> <DropdownMenu slot="list"> <DropdownItem name="default" :selected="tableSize === 'default'">{{ $t('common.default') }}</DropdownItem> <DropdownItem name="large" :selected="tableSize === 'large'">{{ $t('common.large') }}</DropdownItem> <DropdownItem name="small" :selected="tableSize === 'small'">{{ $t('common.small') }}</DropdownItem> </DropdownMenu> </Dropdown> <Tooltip class="ivu-ml" :content="tableFullscreen ? $t('common.exit_fullscreen') : $t('common.fullscreen')" placement="top"> <i-link @click.native="handleFullscreen"> <Icon size="16" :custom="tableFullscreen ? 'i-icon i-icon-exit-full-screen' : 'i-icon i-icon-full-screen'"/> </i-link> </Tooltip> <Tooltip class="ivu-ml" :content="$t('common.refresh')" placement="top"> <i-link @click.native="handleRefresh"> <Icon size="16" custom="i-icon i-icon-refresh"/> </i-link> </Tooltip> <Dropdown trigger="click"> <Tooltip class="ivu-ml" :content="$t('common.col_setting')" placement="top"> <i-link> <Icon size="16" type="md-options"/> </i-link> </Tooltip> <DropdownMenu slot="list"> <div class="ivu-p-8"> <Row> <Col span="12">{{ $t('common.col_show') }}</Col> <Col span="12" class="ivu-text-right"> <i-link link-color @click.native="handleResetColumn">{{ $t('common.reset') }}</i-link> </Col> </Row> </div> <Divider size="small" class="ivu-mt-8 ivu-mb-8"/> <li class="ivu-dropdown-item" v-for="item in columns" :key="item.title" v-if="item.title" @click="item.show = !item.show"> <Checkbox v-model="item.show"></Checkbox> <span>{{ item.title }}</span> </li> </DropdownMenu> </Dropdown> </div> <Table border ref="table" :height="tableHeight" :columns="tableColumns" :row-class-name="rowClassName" :data="datas" :loading="loading" :size="tableSize" class="ivu-mt-8"> <template slot-scope="{ row, index }" slot="nums"> <span class="my-count-primary">{{ row.sns_count }}</span> <ButtonGroup style="float: right" > <Button size="small" type="warning" class="right" @click="handleBatchAdd(row)">{{ $t('sn.add_sn') }}</Button> <Button size="small" type="info" @click="handExport(row)">{{ $t('sn.export_sn') }}</Button> </ButtonGroup> </template> <template slot-scope="{ row, index }" slot="action"> <ButtonGroup> <Button size="small" type="primary" @click="handleEdit(row,index)">{{ $t('common.edit') }}</Button> <Button size="small" type="error" @click="handleDel(row,index)">{{ $t('common.del') }}</Button> </ButtonGroup> </template> </Table> <Row style="text-align: right;margin-top: 5px;margin-bottom: -12px;" ref="pageFooter"> <Page :total="total" :current.sync="page" show-total show-sizer :page-size="pageSize" :page-size-opts="[20,40,60,80,100]" @on-change="pageChange" @on-page-size-change="handleChangePageSize"/> </Row> </Card> <Modal v-model="formModal" width="800" :title="editIndex===-1?$t('sn.add_batch'):$t('sn.edit_batch')" :transfer="false" :footer-hide="true" :mask-closable="false"> <BatchForm v-if="formModal" :formData="formEdit" :loading="loadingSubmit" @on-success-valid="saveData"></BatchForm> </Modal> <Modal v-model="batchAddModal" width="800" :title="$t('sn.batch_add_sn')" :transfer="false" :footer-hide="true" :mask-closable="false" :closable="allowClose"> <BatchAdd v-if="batchAddModal" :sel-batches="selBatches" :isLockBatches="true" @on-lock-close="lockClose" @on-complete="closeBatchModal"></BatchAdd> </Modal> </div> </template> <script> import { httpGetParams } from '@/api/data' import BatchForm from '@/view/sn/lib/batchForm'; import BatchAdd from '@/view/sn/lib/batchAdd'; import excel from '@/libs/excel'; import common from '@/view/mixins/common'; export default { name: 'Batch', mixins: [common], components: { BatchForm, BatchAdd }, data () { return { getDataUrl: '/admin/batches', editUrl: '/admin/batch', delUrl: '/admin/batch', selBatches: {}, searchOption: { intro: { title: this.$t('sn.intro'), type: 'input', value: '' }, nums: { title: this.$t('sn.nums') + this.$t('sn.gt_eq'), type: 'number', value: '' } }, columns: [ { title: 'ID', key: 'id', width: 60, align: 'center', fixed: 'left', show: true }, { title: this.$t('sn.intro'), key: 'intro', minWidth: 150, show: true }, { title: this.$t('sn.nums'), slot: 'nums', minWidth: 150, show: true }, { title: this.$t('common.updated_at'), key: 'updated_at', minWidth: 180, show: true, render: (h, params) => { const date = this.$options.filters.dateformat(params.row.updated_at * 1000) return h('div', date) } }, { title: this.$t('common.operate'), slot: 'action', align: 'center', fixed: 'right', width: 140, show: true } ], formParams: { id: 0, intro: '' }, batchAddModal: false, allowClose: true, exportBatch: 0, excelExportData: [], retry: 0 } }, methods: { lockClose: function () { this.allowClose = false }, handleBatchAdd: function (row) { this.selBatches[row.id] = row.intro this.batchAddModal = true }, closeBatchModal: function () { this.batchAddModal = false if (!this.allowClose) { this.getData() } }, handExport: function (row) { this.$Spin.show({ render: (h) => { return h('div', [ h('Icon', { 'class': 'spin-icon-load', props: { type: 'ios-loading', size: 40 } }), h('div', { style: { 'font-size': '20px' } }, this.$t('sn.export_tip')) ]) } }); this.exportBatch = row.id this.excelExportData = [] this.retry = 0 this.getExportData(1) }, getExportData: function (page) { let searchValue = { page: page, pageSize: 1000 } let _this = this httpGetParams('/admin/sns/' + this.exportBatch, searchValue, null).then(res => { res.data.forEach(item => { _this.excelExportData.push(item) }) if (res.to >= res.total) { _this.excelExport() } else { _this.getExportData(page + 1) } }).catch((err) => { console.log(err) if (_this.retry++ >= 2) { _this.$Spin.hide() _this.$Message.error(this.$t('sn.export_fail')) _this.excelExportData = [] } else { _this.getExportData(page) } }) }, excelExport: function () { const title = [this.$t('sn.sn')] const key = ['sn'] excel.export_array_to_excel({ key, data: this.excelExportData, title, filename: this.$t('sn.sn') + '-' + this.exportBatch + '-' + (new Date()).getTime(), autoWidth: true }) this.$Spin.hide() } }, created () { this.getData() } } </script> <style lang="less"> .my-count-primary{ color:#ff9900; font-weight: bold; } </style>
文章评论