混入 (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()
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
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)
}
})
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子
之前调用。
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()
vm.bar()
vm.conflicting()
注意:
Vue.extend()
也使用同样的策略进行合并。
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响
每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: '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>
评论区