1.vue2表格的封装使用
表格使用
<standard-tables:columns="columns":dataSource="dataSource":loading="loading"bordered:pagination="{ ...pagination, onChange: onPageChange }"><div slot="warnType" slot-scope="{ text, record }"><span>{{ text | textList(warnTypeData) }}</span></div><div slot="warnLevel" slot-scope="{ text, record }"><span>{{ text | textList(warnLevelData) }}</span></div><div slot="status" slot-scope="{ text, record }"><span v-if="text == 0">待指派</span><span v-if="text == 1">待处置</span><span v-if="text == 2">已处置</span></div><div slot="action" slot-scope="{ text, record }"><a @click="viewRecord(record)"> 详情 </a><a-divider type="vertical" /><a @click="editRecord(record)" v-if="record.status != 2"> 指派 </a></div></standard-tables>pagination: {current: 1,pageSize: 10,total: 0,showTotal: (total) => `共 ${total} 条数据`, // 展示总共有几条数据},//方法onPageChange(page, pageSize) {this.pagination.current = page;this.pagination.pageSize = pageSize;this.getData();},
表格封装组件的模样
<template><div class="standard-table"><a-table:bordered="bordered":loading="loading":columns="columns":dataSource="dataSource":rowKey="(record, index) => {return index;}":pagination="pagination":expandedRowKeys="expandedRowKeys":expandedRowRender="expandedRowRender"@change="onChange"><templateslot-scope="text, record, index":slot="slot"v-for="slot in Object.keys($scopedSlots).filter((key) => key !== 'expandedRowRender')"><slot :name="slot" v-bind="{ text, record, index }"></slot></template><template :slot="slot" v-for="slot in Object.keys($slots)"><slot :name="slot"></slot></template><templateslot-scope="record, index, indent, expanded":slot="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"><slotv-bind="{ record, index, indent, expanded }":name="$scopedSlots.expandedRowRender ? 'expandedRowRender' : ''"></slot></template></a-table></div>
</template><script>
export default {name: "StandardTables",props: {bordered: Boolean,loading: [Boolean, Object],columns: Array,dataSource: Array,rowKey: {type: [String, Function],default: "key",},pagination: {type: [Object, Boolean],default: false,},selectedRows: Array,expandedRowKeys: Array,expandedRowRender: Function,},data() {return {needTotalList: [],};},methods: {equals(record1, record2) {if (record1 === record2) {return true;}// 使用解构赋值从当前组件实例(this)中提取rowKey属性。// rowKey通常用于标识对象的独特性,类似于数据库中的主键const { rowKey } = this;// 检查rowKey是否存在且其类型为字符串。如果满足条件,说明我们可以通过rowKey属性来判断两个对象是否相等。if (rowKey && typeof rowKey === "string") {return record1[rowKey] === record2[rowKey];}// 如果rowKey存在且是函数,这意味着用户提供了自定义的比较函数。else if (rowKey && typeof rowKey === "function") {// 执行这个自定义函数,将record1和record2分别作为参数传入,然后比较函数返回值是否相等。return rowKey(record1) === rowKey(record2);}return false;},contains(arr, item) {if (!arr || arr.length === 0) {return false;}console.log("this", this);const { equals } = this;for (let i = 0; i < arr.length; i++) {if (equals(arr[i], item)) {return true;}}return false;},onSelectAll(selected, rows) {const { getKey, contains } = this;const unselected = this.dataSource.filter((item) => !contains(rows, item, this.rowKey));const _selectedRows = this.selectedRows.filter((item) => !contains(unselected, item, this.rowKey));const set = {};_selectedRows.forEach((item) => (set[getKey(item)] = item));rows.forEach((item) => (set[getKey(item)] = item));const _rows = Object.values(set);this.$emit("update:selectedRows", _rows);this.$emit("selectedRowChange",_rows.map((item) => getKey(item)),_rows);},getKey(record) {const { rowKey } = this;if (!rowKey || !record) {return undefined;}if (typeof rowKey === "string") {return record[rowKey];} else {return rowKey(record);}},onSelect(record, selected) {const { equals, selectedRows, getKey } = this;const _selectedRows = selected? [...selectedRows, record]: selectedRows.filter((row) => !equals(row, record));this.$emit("update:selectedRows", _selectedRows);this.$emit("selectedRowChange",_selectedRows.map((item) => getKey(item)),_selectedRows);},initTotalList(columns) {return columns.filter((item) => item.needTotal).map((item) => {return {...item,total: 0,};});},onClear() {this.$emit("update:selectedRows", []);this.$emit("selectedRowChange", [], []);this.$emit("clear");},onChange(pagination, filters, sorter, { currentDataSource }) {this.$emit("change", pagination, filters, sorter, { currentDataSource });},},created() {this.needTotalList = this.initTotalList(this.columns);},watch: {selectedRows(selectedRows) {this.needTotalList = this.needTotalList.map((item) => {return {...item,total: selectedRows.reduce((sum, val) => {let v;try {v = val[item.dataIndex]? val[item.dataIndex]: eval(`val.${item.dataIndex}`);} catch (_) {v = val[item.dataIndex];}v = !isNaN(parseFloat(v)) ? parseFloat(v) : 0;return sum + v;}, 0),};});},},computed: {selectedRowKeys() {return this.selectedRows.map((record) => this.getKey(record));},},
};
</script><style scoped lang="less">
.standard-table {.alert {margin-bottom: 16px;.message {a {font-weight: 600;}}.clear {float: right;}}
}
</style>
2.vue2图片上传组件封装
使用
<imgUpload v-model="assetsData.imageUrl"></imgUpload>
封装代码
<template><div><div class="clearfix"><a-upload:disabled="disabled"name="file"list-type="picture-card"class="avatar-uploader":show-upload-list="false"action="/upms/file/upload?fileType=image&dir=material/":before-upload="beforeUpload":headers="headers"@change="upchangeImg"><imgv-if="imgUrl":src="imgUrl"alt="avatar"style="height: 100px; width: 100px"/><div v-else><a-icon :type="loading ? 'loading' : 'plus'" /><div class="ant-upload-text">上传</div></div></a-upload><a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel"><img alt="example" style="width: 100%" :src="previewImage" /></a-modal></div></div>
</template><script>
//注意!!!!!
//在modal弹窗组件中使用该组件需要在关闭弹窗方法里清空数据否则会报错
import Cookie from "js-cookie";
export default {name: "vorganTree",props: {value: {type: String,default: undefined,},disabled: {type: Boolean,default: false,},placeholder: {type: String,default: "请选择所属组织",},},data() {return {headers: {// 请求头部Authorization: Cookie.get("Authorization"),},imgUrl: this.value,loading: false,previewVisible: false,previewImage: "",};},watch: {// value: {// handler(newVal) {// this.imgUrl = newVal;// },// immediate: true// }value(val) {this.imgUrl = val;},},methods: {// 图片handleCancel() {this.previewVisible = false;},// 图片上传的函数beforeUpload(file) {const isJpgOrPng =file.type === "image/jpeg" || file.type === "image/png";if (!isJpgOrPng) {this.$message.error("只能上传jpeg和png格式的图片!");}const isLt2M = file.size / 1024 / 1024 < 2;if (!isLt2M) {this.$message.error("图片必须小于2MB!");}return isJpgOrPng && isLt2M;},upchangeImg(info) {// handleChange2 (info) {if (info.file.status === "uploading") {this.loading = true;return;}if (info.file.status === "done") {this.imgUrl = info.file.response.link;this.onChange(info.file.response.link);this.loading = false;}if (info.file.status === "error") {this.$message.error("上传失败,请重试!");this.loading = false;}},onChange(value) {console.log("111111111111", value);// this.$emit("update:value", value);this.$emit("input", value);this.$emit("change", value);this.$emit("change:value", value);},},
};
</script><style scoped lang="less"></style>
预览
<imgstyle="width: 100%":src="basicData.imageUrl":preview="basicData.imageUrl"/>
3.vue2树形下拉框封装
使用
<a-form-item label="所属单位:"><vorganTreeplaceholder="请选择"v-model="assetsData.orgId"@change="getOrg"></vorganTree></a-form-item>getUsrOrg(e, e2) {this.assetsData.useOrgName = e2[0];},
封装的代码
<template><div><a-tree-select v-model="orgId" style="width: 100%" :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }":placeholder="placeholder" allow-clear :disabled="disabled" :tree-data="vorganTreeData":replaceFields="replaceFields" @change="onChange"></a-tree-select><!-- tree-default-expand-all --></div>
</template><script>
//注意!!!!!
//在modal弹窗组件中使用该组件需要在关闭弹窗方法里清空数据否则会报错
import { getVorganTree } from "@/api/index"export default {name: 'vorganTree',props: {value: {type: String,default: undefined,},disabled: {type: Boolean,default: false,},placeholder: {type: String,default: '请选择'},replaceFields: {type: Object,default: () => {return {children: 'children',title: 'name',key: 'id',value: 'id',}},},},data () {return {orgId: this.value,vorganTreeData: [],}},watch: {value: {handler (newVal) {this.orgId = newVal},immediate: true,},},mounted () {this.getVorganTree()},methods: {getVorganTree () {getVorganTree().then(res => {this.vorganTreeData = res.data})},selectClear () {this.orgId = undefined},onChange (value,label) {this.$emit('update:value', value)this.$emit('change', value,label)},},model: {prop: 'value',event: 'change'}
}
</script><style scoped lang="less">
</style>
4.vue2单选表格封装
使用
<selectUserv-model="assetsData.userId"@change="getUser"></selectUser>getUser(e) {this.assetsData.userName = e[0].nickName;},
封装代码分成3块,一个嵌套一个
第一步
<template><!-- 定义在这里的参数都是不可在外部覆盖的,防止出现问题 --><input-select:value="value":ellipsisLength="25":listUrl="url.list":columns="columns"v-on="$listeners"v-bind="attrs":disabled="disabled"@change="change"/>
</template><script>
import inputSelect from "@/components/inputSelect/index.vue";export default {name: "selectTop",components: { inputSelect },props: {value: {type: String,default: "",},disabled: {type: Boolean,default: false,},},data() {return {url: { list: "/upms/user/page" },columns: [{title: "用户名称",align: "center",width: "60%",widthRight: "70%",dataIndex: "nickName",},{ title: "手机号", align: "center", width: "40%", dataIndex: "phone" },],// 定义在这里的参数都是可以在外部传递覆盖的,可以更灵活的定制化使用的组件default: {name: "用户",width: "80%",displayKey: "nickName",returnKeys: ["id", "nickName"],rowKey: "id",valueKey: "id",queryParamText: "用户名称",queryParamCode: "nickName",multiple: false,buttons: false,},};},computed: {attrs() {const a = Object.assign(this.default, this.$attrs);console.log("a", a);return a;},},methods: {change(val) {console.log(val);this.$emit("changes", val);},},
};
</script><style lang="less" scoped></style>
第二步
<template><a-row class="j-select-biz-component-box" type="flex" :gutter="8"><a-col class="left" :class="{ full: !buttons }"><slot name="left"><a-selectmode="multiple":placeholder="placeholder"v-model="selectValue":options="selectOptions"allowClear:disabled="disabled":open="selectOpen"style="width: 100%"@dropdownVisibleChange="handleDropdownVisibleChange"@click.native="visible = buttons ? visible : true"/></slot></a-col><a-col v-if="buttons" class="right"><a-buttontype="primary"icon="search":disabled="disabled"@click="visible = true">{{ selectButtonText }}</a-button></a-col><select-modalv-model="selectValue":visible.sync="visible"v-bind="modalProps"@options="handleOptions"/></a-row>
</template><script>
import selectModal from "./selectModal";export default {name: "JSelectBizComponent",components: { selectModal },props: {value: {type: String,default: "",},/** 是否返回 id,默认 false,返回 code */returnId: {type: Boolean,default: false,},placeholder: {type: String,default: "请选择",},disabled: {type: Boolean,default: false,},// 是否支持多选,默认 truemultiple: {type: Boolean,default: true,},// 是否显示按钮,默认 truebuttons: {type: Boolean,default: true,},// 显示的 KeydisplayKey: {type: String,default: null,},// 返回的 keyreturnKeys: {type: Array,default: () => ["id", "id"],},// 选择按钮文字selectButtonText: {type: String,default: "选择",},},data() {return {selectValue: [],selectOptions: [],dataSourceMap: {},visible: false,selectOpen: false,};},computed: {valueKey() {return this.returnId ? this.returnKeys[0] : this.returnKeys[1];},modalProps() {return Object.assign({valueKey: this.valueKey,multiple: this.multiple,returnKeys: this.returnKeys,displayKey: this.displayKey || this.valueKey,},this.$attrs);},},mounted() {console.log("Received props:", this.$props);},watch: {value: {immediate: true,handler(val) {if (val) {this.selectValue = val.split(",");} else {this.selectValue = [];}},},selectValue: {deep: true,handler(val) {let rows = val.map((key) => this.dataSourceMap[key]);let data = val.join(",");if (data !== this.value) {this.$emit("select", rows);this.$emit("input", data);this.$emit("change", rows);}},},},methods: {handleOptions(options, dataSourceMap) {console.log("options", options, "dataSourceMap", dataSourceMap);this.selectOptions = options;this.dataSourceMap = dataSourceMap;},handleDropdownVisibleChange() {// 解决antdv自己的bug —— open 设置为 false 了,点击后还是添加了 open 样式,导致点击事件失效this.selectOpen = true;this.$nextTick(() => {this.selectOpen = false;});},},
};
</script><style lang="less" scoped>
.j-select-biz-component-box {@width: 82px;.left {width: calc(100% - @width - 8px);}.right {width: @width;}.full {width: 100%;}/deep/ .ant-select-search__field {display: none !important;}
}
</style>
第三步
<template><j-modalcentered:title="name + '选择'":width="width":visible="visible"switchFullscreen@ok="handleOk"@cancel="close"cancelText="关闭"><a-row :gutter="18"><a-col :span="16"><!-- 查询区域 --><div class="table-page-search-wrapper"><a-form layout="inline"><a-row :gutter="24"><a-col :span="14"><a-form-item :label="queryParamText || name"><a-inputv-model="queryParam[queryParamCode || valueKey]":placeholder="'请输入' + (queryParamText || name)"@pressEnter="searchQuery"/></a-form-item></a-col><a-col :span="8"><spanstyle="float: left; overflow: hidden"class="table-page-search-submitButtons"><a-button type="primary" @click="searchQuery" icon="search">查询</a-button><a-buttontype="primary"@click="searchReset"icon="reload"style="margin-left: 8px">重置</a-button></span></a-col></a-row></a-form></div><a-tablesize="middle"bordered:rowKey="rowKey":columns="innerColumns":dataSource="dataSource":pagination="ipagination":loading="loading":scroll="{ y: 240 }":rowSelection="{selectedRowKeys,onChange: onSelectChange,type: multiple ? 'checkbox' : 'radio',}":customRow="customRowFn"@change="handleTableChange"></a-table></a-col><a-col :span="8"><a-card:title="'已选' + name":bordered="false":head-style="{ padding: 0 }":body-style="{ padding: 0 }"><a-tablesize="middle":rowKey="rowKey"borderedv-bind="selectedTable"><span slot="action" slot-scope="text, record, index"><a @click="handleDeleteSelected(record, index)">删除</a></span></a-table></a-card></a-col></a-row></j-modal>
</template><script>
import { getAction } from "@/api/manage";
import Ellipsis from "@/components/jeecg/Ellipsis";
import JModal from "@/components/jeecg/JModal";
import { listSelect } from "@/mixins/listSelect";
import { cloneObject, pushIfNotExist } from "@/utils/util";export default {name: "JSelectBizComponentModal",mixins: [listSelect],components: { Ellipsis, JModal },props: {value: {type: Array,default: () => [],},visible: {type: Boolean,default: false,},valueKey: {type: String,required: true,},multiple: {type: Boolean,default: true,},width: {type: [Number, String],default: "80%",},name: {type: String,default: "",},listUrl: {type: String,required: true,default: "",},// 根据 value 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realnamevalueUrl: {type: String,default: "",},displayKey: {type: String,default: null,},columns: {type: Array,required: true,default: () => [],},// 查询条件CodequeryParamCode: {type: String,default: null,},// 查询条件文字queryParamText: {type: String,default: null,},rowKey: {type: String,default: "id",},// 过长裁剪长度,设置为 -1 代表不裁剪ellipsisLength: {type: Number,default: 12,},},data() {return {innerValue: [],// 已选择列表selectedTable: {pagination: false,scroll: { y: 240 },columns: [{...this.columns[0],width: this.columns[0].widthRight || this.columns[0].width,},{title: "操作",dataIndex: "action",align: "center",width: 60,scopedSlots: { customRender: "action" },},],dataSource: [],},renderEllipsis: (value) => (<ellipsis length={this.ellipsisLength}>{value}</ellipsis>),url: { list: this.listUrl },/* 分页参数 */ipagination: {current: 1,pageSize: 5,pageSizeOptions: ["5", "10", "20", "30"],showTotal: (total, range) => {return range[0] + "-" + range[1] + " 共" + total + "条";},showQuickJumper: true,showSizeChanger: true,total: 0,},options: [],dataSourceMap: {},};},computed: {// 表头innerColumns() {console.log("this.columns", this.columns);let columns = cloneObject(this.columns);columns.forEach((column) => {// 给所有的列加上过长裁剪if (this.ellipsisLength !== -1) {column.customRender = (text) => this.renderEllipsis(text);}});return columns;},},watch: {value: {deep: true,immediate: true,handler(val) {this.innerValue = cloneObject(val);this.selectedRowKeys = [];this.valueWatchHandler(val);this.queryOptionsByValue(val);},},dataSource: {deep: true,handler(val) {this.emitOptions(val);this.valueWatchHandler(this.innerValue);},},selectedRowKeys: {immediate: true,deep: true,handler(val) {this.selectedTable.dataSource = val.map((key) => {for (let data of this.dataSource) {if (data[this.rowKey] === key) {pushIfNotExist(this.innerValue, data[this.valueKey]);return data;}}for (let data of this.selectedTable.dataSource) {if (data[this.rowKey] === key) {pushIfNotExist(this.innerValue, data[this.valueKey]);return data;}}console.warn("未找到选择的行信息,key:" + key);return {};});},},},methods: {/** 关闭弹窗 */close() {this.$emit("update:visible", false);},valueWatchHandler(val) {val.forEach((item) => {this.dataSource.concat(this.selectedTable.dataSource).forEach((data) => {if (data[this.valueKey] === item) {pushIfNotExist(this.selectedRowKeys, data[this.rowKey]);}});});},queryOptionsByValue(value) {if (!value || value.length === 0) {return;}// 判断options是否存在value,如果已存在数据就不再请求后台了let notExist = false;for (let val of value) {let find = false;for (let option of this.options) {if (val === option.value) {find = true;break;}}if (!find) {notExist = true;break;}}if (!notExist) return;getAction(this.valueUrl || this.listUrl, {// 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确[this.valueKey]: value.join(",") + ",",pageNo: 1,pageSize: value.length,}).then((res) => {console.log(res);if (res.success) {let dataSource = res;if (!(dataSource instanceof Array)) {dataSource = res.records;}this.emitOptions(dataSource, (data) => {pushIfNotExist(this.innerValue, data[this.valueKey]);pushIfNotExist(this.selectedRowKeys, data[this.rowKey]);pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey);});}});},emitOptions(dataSource, callback) {dataSource.forEach((data) => {let key = data[this.valueKey];this.dataSourceMap[key] = data;pushIfNotExist(this.options,{ label: data[this.displayKey || this.valueKey], value: key },"value");typeof callback === "function" ? callback(data) : "";});this.$emit("options", this.options, this.dataSourceMap);},/** 完成选择 */handleOk() {let value = this.selectedTable.dataSource.map((data) => data[this.valueKey]);this.$emit("input", value);this.close();},/** 删除已选择的 */handleDeleteSelected(record, index) {this.selectedRowKeys.splice(this.selectedRowKeys.indexOf(record[this.rowKey]),1);this.selectedTable.dataSource.splice(index, 1);},customRowFn(record) {return {on: {click: () => {let key = record[this.rowKey];if (!this.multiple) {this.selectedRowKeys = [key];this.selectedTable.dataSource = [record];} else {let index = this.selectedRowKeys.indexOf(key);if (index === -1) {this.selectedRowKeys.push(key);this.selectedTable.dataSource.push(record);} else {this.handleDeleteSelected(record, index);}}},},};},},
};
</script>
<style lang="less" scoped>
</style>
<template><a-modalref="modal":class="getClass(modalClass)":style="getStyle(modalStyle)":visible="visible"v-bind="_attrs"v-on="$listeners"@ok="handleOk"@cancel="handleCancel"><slot></slot><template v-if="!isNoTitle" slot="title"><a-row class="j-modal-title-row" type="flex"><a-col class="left"><slot name="title">{{ title }}</slot></a-col><a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen"><a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/></a-col></a-row></template><!-- 处理 scopedSlots --><template v-for="slotName of scopedSlotsKeys" :slot="slotName"><slot :name="slotName"></slot></template><!-- 处理 slots --><template v-for="slotName of slotsKeys" v-slot:[slotName]><slot :name="slotName"></slot></template></a-modal>
</template><script>import { getClass, getStyle } from '@/utils/props-util'import { triggerWindowResizeEvent } from '@/utils/util'export default {name: 'JModal',props: {title: String,// 可使用 .sync 修饰符visible: Boolean,// 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符fullscreen: {type: Boolean,default: false},// 是否允许切换全屏(允许后右上角会出现一个按钮)switchFullscreen: {type: Boolean,default: false},// 点击确定按钮的时候是否关闭弹窗okClose: {type: Boolean,default: true},},data() {return {// 内部使用的 slots ,不再处理usedSlots: ['title'],// 实际控制是否全屏的参数innerFullscreen: this.fullscreen,}},computed: {// 一些未处理的参数或特殊处理的参数绑定到 a-modal 上_attrs() {let attrs = { ...this.$attrs }// 如果全屏就将宽度设为 100%if (this.innerFullscreen) {attrs['width'] = '100%'}return attrs},modalClass() {return {'j-modal-box': true,'fullscreen': this.innerFullscreen,'no-title': this.isNoTitle,'no-footer': this.isNoFooter,}},modalStyle() {let style = {}// 如果全屏就将top设为 0if (this.innerFullscreen) {style['top'] = '0'}return style},isNoTitle() {return !this.title && !this.allSlotsKeys.includes('title')},isNoFooter() {return this._attrs['footer'] === null},slotsKeys() {return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))},scopedSlotsKeys() {return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))},allSlotsKeys() {return this.slotsKeys.concat(this.scopedSlotsKeys)},// 切换全屏的按钮图标fullscreenButtonIcon() {return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'},},watch: {visible() {if (this.visible) {this.innerFullscreen = this.fullscreen}},innerFullscreen(val) {this.$emit('update:fullscreen', val)},},methods: {getClass(clazz) {return { ...getClass(this), ...clazz }},getStyle(style) {return { ...getStyle(this), ...style }},close() {this.$emit('update:visible', false)},handleOk() {if (this.okClose) {this.close()}},handleCancel() {this.close()},/** 切换全屏 */toggleFullscreen() {this.innerFullscreen = !this.innerFullscreentriggerWindowResizeEvent()},}}
</script><style lang="less">.j-modal-box {&.fullscreen {top: 0;left: 0;padding: 0;// 兼容1.6.2版本的antdv& .ant-modal {top: 0;padding: 0;height: 100vh;}& .ant-modal-content {height: 100vh;border-radius: 0;& .ant-modal-body {/* title 和 footer 各占 55px */height: calc(100% - 55px - 55px);overflow: auto;}}&.no-title, &.no-footer {.ant-modal-body {height: calc(100% - 55px);}}&.no-title.no-footer {.ant-modal-body {height: 100%;}}}.j-modal-title-row {.left {width: calc(100% - 56px - 56px);}.right {width: 56px;position: inherit;.ant-modal-close {right: 56px;color: rgba(0, 0, 0, 0.45);&:hover {color: rgba(0, 0, 0, 0.75);}}}}}@media (max-width: 767px) {.j-modal-box.fullscreen {margin: 0;max-width: 100vw;}}
</style>
5.vue2表格如果内容有图片需要放大
<a-tablerowKey="id"bordered:columns="columns":data-source="dataSource"@change="handleTableChange":loading="loading":pagination="ipagination"><template slot="avatarslot" slot-scope="text, record, index"><div class="anty-img-wrap"><a-avatarshape="square":src="record.imageUrl"@click="handlePreviewPicture(record)"icon="picture"/></div></template><span slot="status" slot-scope="status"><a-tag:color="status == '0'? 'green': status == 1? 'blue': status == 2? 'volcano': status == 3? 'purple': 'pink'">{{ status | statusText }}</a-tag></span></a-table>// 图片handleCancel() {this.previewVisible = false;},// 图片查看弹窗弹出handlePreviewPicture(record) {if (record.imageUrl) {this.previewVisible = true;this.previewImage = record.imageUrl;} else {this.$message.info("无图片");}},
6.样式
效果图
<a-card class="card" title="资产使用轨迹" :bordered="false"><div class="itembox"><div v-for="(item, index) of datalist2" :key="index"><div class="content"><b>{{ item.operateType | statusText }}</b><span>操作人:{{ item.creator }}</span><span>{{ item.createTime }}</span><div :class="index < datalist2.length - 1 ? 'arrow' : ''"></div></div></div></div></a-card>
7.vue2步骤条组件封装
效果图
使用
<ApprovalProgress :ininstanceId="instanceId"></ApprovalProgress>
封装
<template><div style="margin-left: 30px"><!-- <a-card title="审批进度" style="margin-top: 10px"><a-table size="default" :columns="columns" :data-source="schedule" bordered><span slot="action" slot-scope="text, record" class="flx-row-c-c">{{ record.result | listText(resultList) }}</span></a-table></a-card> --><a-steps progress-dot :current="current" direction="vertical"><a-step v-for="(item, index) of schedule" :key="index"><template v-slot:title><div style="display: flex"><div>{{ item.name }}</div><div style="margin-left: 10px; font-size: 12px; line-height: 23px">{{ item.result | listText(resultList) }}</div><div style="margin-left: 10px; font-size: 12px; line-height: 23px">{{ item.endTime }}</div></div></template><template v-slot:description><div style="display: flex; margin: 5px" v-for="(item2, index2) of item.assignees" :key="index2"><div style="">{{ item2.username }}</div><div style="margin-left: 20px">{{ item2.isExecutor ? "已处理" : "未处理" }}</div></div></template></a-step></a-steps></div>
</template>
<script>
import { historicFlow } from "@/api/wms";
export default {data() {return {schedule: [],Svgimg: "",columns: [{title: "时间",align: "center",dataIndex: "createTime",},{title: "操作人",align: "center",dataIndex: "username",},{title: "操作",dataIndex: "action",align: "center",scopedSlots: { customRender: "action" },},],current: 0,resultList: [{label: "待审核",value: "0",},{label: "处理中",value: "1",},{label: "已同意",value: "2",},{label: "驳回",value: "3",},{label: "撤回",value: "4",},{label: "删除",value: "5",},],};},props: {ininstanceId: {type: String,default: "",},Initiator: {type: String,default: "",},},watch: {ininstanceId: {immediate: true,handler(val) {this.getSchedule(val);},},},filters: {listText(val, list) {let mm = "";list.forEach((item) => {if (item.value == val) {mm = item.label;}});return mm;},},methods: {getSchedule(val) {historicFlow(val).then((res) => {let row = res.data;let index = row.findLastIndex((item) => item.assignees && item.assignees.some((item2) => item2.isExecutor === true));row.unshift({name: "发起",});row.push({name: "结束",});this.current = index + 2;this.schedule = row;});},},
};
</script>
<style lang="less" scoped>
/deep/ .ant-steps-dot .ant-steps-item-content,
.ant-steps-dot.ant-steps-small .ant-steps-item-content {width: 16.292vw;
}/deep/ .ant-steps-vertical .ant-steps-item-content {display: block;min-height: 5.5vw;overflow: hidden;
}
</style>
8.vue2表格总封装
代码
import T from "ant-design-vue/es/table/Table";
import get from "lodash.get"
export default {data() {return {needTotalList: [],selectedRows: [],selectedRowKeys: [],localLoading: false,localDataSource: [],localPagination: Object.assign({}, T.props.pagination)};},props: Object.assign({}, T.props, {rowKey: {type: [String, Function],default: 'id'},data: {type: Function,required: true},pageNum: {type: Number,default: 1},pageSize: {type: Number,default: 10},showSizeChanger: {type: Boolean,default: true},showAlertInfo: {type: Boolean,default: false},showPagination: {default: 'auto'},deleteShow: { // 删除键type: Boolean,default: false},bordered: {type: Boolean,default: true},}),watch: {'localPagination.current'(val) {this.$router.push({name: this.$route.name,params: Object.assign({}, this.$route.params, {current: val}),});},pageNum(val) {Object.assign(this.localPagination, {current: val});},pageSize(val) {Object.assign(this.localPagination, {pageSize: val});},showSizeChanger(val) {Object.assign(this.localPagination, {showSizeChanger: val});}},zzh(){//const that = this.call(this);const that = thisreturn that;},created() {this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {current: this.pageNum,pageSize: this.pageSize,showSizeChanger: this.showSizeChanger});this.needTotalList = this.initTotalList(this.columns)this.loadData();},methods: {// 加载状态loadingShow(value) {this.localLoading = value},refresh() {var mm = {current: 1,total: 0,size: this.pageSize,}this.loadData(mm);},// 刷新删除deleteRefresh() {this.loadData()},loadData(pagination, filters, sorter) {this.localLoading = truevar result = this.data(Object.assign({current: (pagination && pagination.current) ||this.localPagination.current,size: (pagination && pagination.pageSize) ||this.localPagination.pageSize},(sorter && sorter.field && {sortField: sorter.field}) || {},(sorter && sorter.order && {sortOrder: sorter.order}) || {}, {...filters}));if (result instanceof Promise) {result.then(r => {this.localPagination = Object.assign({}, this.localPagination, {current: (pagination && pagination.current) ||this.localPagination.current, // 返回结果中的当前分页数total: r.totalCount, // 返回结果中的总记录数showSizeChanger: this.showSizeChanger,pageSize: (pagination && pagination.pageSize) ||this.localPagination.pageSize});!r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)this.localDataSource = r.data; // 返回结果中的数组数据this.localLoading = false});}},initTotalList(columns) {const totalList = []columns && columns instanceof Array && columns.forEach(column => {if (column.needTotal) {totalList.push({ ...column,total: 0})}})return totalList},updateSelect(selectedRowKeys, selectedRows) {this.selectedRowKeys = selectedRowKeysthis.selectedRows = selectedRowslet list = this.needTotalListthis.needTotalList = list.map(item => {return {...item,total: selectedRows.reduce((sum, val) => {let total = sum + get(val, item.dataIndex)return isNaN(total) ? 0 : total}, 0)}})// this.$emit('change', selectedRowKeys, selectedRows)},updateEdit() {this.selectedRows = []},onClearSelected() {this.selectedRowKeys = []this.selectedRows = []this.updateSelect([], [])this.$emit('emptyData',{selectedRowKeys:[],selectedRows:[]})this.$emit('onSelect', { selectedRowKeys: [], selectedRows: [] })},onDeleteSelected() {this.$emit('DeleteData')},renderMsg(h) {const _vm = thisthis.that = thislet d = []// 构建 已选择// d.push(// h('span', {// style: {// marginRight: '12px'// }// }, ['已选择 ', h('a', {// style: {// fontWeight: 600// }// }, this.selectedRows.length)])// );// // 构建 列统计// this.needTotalList.map(item => {// d.push(h('span', {// style: {// marginRight: '12px'// }// },// [// `${ item.title }总计 `,// h('a', {// style: {// fontWeight: 600// }// }, `${ !item.customRender ? item.total : item.customRender(item.total) }`)// ]))// });// // 构建 清空选择// d.push(h('a', {// style: {// marginLeft: '24px'// },// on: {// click: _vm.onClearSelected// }// }, '清空'))// if(this.deleteShow){// // 构建 清空选择// d.push(h('a', {// style: {// marginLeft: '24px',// color: '#DC564A'// },// on: {// click: _vm.onDeleteSelected// }// }, '删除'))// }return d},renderAlert(h) {return h('span', {slot: 'message'}, this.renderMsg(h))},},render(h) {const _vm = thislet props = {},localKeys = Object.keys(this.$data);Object.keys(T.props).forEach(k => {let localKey = `local${k.substring(0,1).toUpperCase()}${k.substring(1)}`;if (localKeys.includes(localKey)) {return props[k] = _vm[localKey];}return props[k] = _vm[k];})// 显示信息提示if (this.showAlertInfo) {props.rowSelection = {selectedRowKeys: this.selectedRowKeys,onChange: (selectedRowKeys, selectedRows) => {_vm.updateSelect(selectedRowKeys, selectedRows)_vm.$emit('onSelect', { selectedRowKeys: selectedRowKeys, selectedRows: selectedRows })}};return h('div', {}, [h("a-table", {tag: "component",attrs: props,on: {change: _vm.loadData},scopedSlots: this.$scopedSlots}, this.$slots.default)]);}return h("a-table", {tag: "component",attrs: props,on: {change: _vm.loadData},scopedSlots: this.$scopedSlots}, this.$slots.default);},
};
考虑前面有没有单选框 有的话加
:showAlertInfo="true"
<s-tableref="tableAssets"size="default":columns="columns4":data="loadDataListAssets":showAlertInfo="true"@onSelect="onChangeAssetsList"></s-table>//方法loadDataListAssets: (parameter) => {return listAssets(Object.assign(parameter, this.listAssetsParam)).then((res) => {var mm = { data: [] };mm.size = res.data.size;mm.current = res.data.current;mm.totalCount = res.data.total;mm.totalPage = res.data.pages;mm.data = res.data.records;return mm;});},// 选择资产资产查询列表-选择onChangeAssetsList(row) {this.AssetsListRow = row.selectedRows;console.log(row.selectedRows);},
9.vue2单选框配合确认框样式
效果图
代码
<a-tableborderedrowKey="id":pagination="false":columns="columns":data-source="dataSource"><spanslot="ifNoOrderRecieve"slot-scope="text, record, index"class="flx-row-c-c"><div><a-popconfirmtitle="是否修改允许无订单订货?"ok-text="确定"cancel-text="取消"@confirm="confirmA(record, index)"@cancel="cancelA"><a-checkboxstyle="margin-left: 20px"v-model="dataSource[index].ifNoOrderRecieve"></a-checkbox></a-popconfirm></div></span><spanslot="ifOrderExceedApply"slot-scope="text, record, index"class="flx-row-c-c"><div><a-popconfirmtitle="是否订单数量超过申请数量?"ok-text="确定"cancel-text="取消"@confirm="confirmB(record, index)"@cancel="cancelB"><a-checkboxstyle="margin-left: 20px"v-model="dataSource[index].ifOrderExceedApply"></a-checkbox></a-popconfirm></div> </span></a-table>
confirmA(record, index) {let self = this;self.dataSource[index].ifNoOrderRecieve = !record.ifNoOrderRecieve;let data = {id: record.id ? record.id : null,organId: record.organId,ifNoOrderRecieve: self.dataSource[index].ifNoOrderRecieve ? "1" : "0",ifOrderExceedApply: record.ifOrderExceedApply ? "1" : "0",};organconfig(data).then((res) => {console.log(res);this.$message.success("修改成功");this.getList();});},cancelA() {},
10.下载导出模板
import { downloadFile } from "@/api/manage";
export function downloadFile(url, fileName, parameter,method) {return downFile(url, parameter,method).then((data) => {// if (!data || data.size === 0) {// Vue.prototype['$message'].warning('文件下载失败')// return// }if (typeof window.navigator.msSaveBlob !== "undefined") {window.navigator.msSaveBlob(new Blob([data]), fileName);} else {let url = window.URL.createObjectURL(new Blob([data]));let link = document.createElement("a");link.style.display = "none";link.href = url;link.setAttribute("download", fileName);document.body.appendChild(link);link.click();document.body.removeChild(link); //下载完成移除元素window.URL.revokeObjectURL(url); //释放掉blob对象}});
}//导出模板getExport() {downloadFile("/asset/entryrecord/exportTemplateXls","财务信息模板.xlsx",{},"post");},
或者另一种方式
导入api
const warehousingExport = (fileName, params) => downloadFile("/asset/assetsinfo/export", fileName, params, "get"); // 导出资产列表
<a-buttontype="primary"@click="getWarehousingExport"style="margin-left: 8px">导出资产</a-button>getWarehousingExport() {var mm = {};mm.id = this.selectedRows.map((item) => {return item.materialCode;});warehousingExport("资产列表", mm).then((res) => {// let execlName = "资产文件名称";// const buf = Buffer.from(res),// blob = new Blob([buf], { type: "application/vnd.ms-excel" }),// downloadElement = document.createElement('a'),// href = window.URL.createObjectURL(blob); // 创建下载的链接// downloadElement.href = href;// downloadElement.download = `${execlName}.xls`; // 下载后文件名// document.body.appendChild(downloadElement);// downloadElement.click(); // 点击下载// window.URL.revokeObjectURL(href); // 释放掉blob对象});},
11.文件打印
导入api
const printAssetList = (fileName, params) => downloadFile("/asset/warehousing/printAssetList", fileName, params, "pdf"); // 打印资产标签列表
import {printAssetList,
} from "@/api/apis";
<a-buttontype="primary"@click="clickAssetsPrint":loading="assetsPrintLoading"style="margin-left: 8px">资产打印</a-button>
assetsPrintLoading: false,// 打印资产clickAssetsPrint() {this.assetsPrintLoading = true;var mm = {};mm.id = this.selectedRows.map((item) => {return item.materialCode;});printAssetList("资产标签", mm).then((res) => {this.assetsPrintLoading = false;}).catch((err) => {this.assetsPrintLoading = false;});// this.$refs.barCode.transferData(this.selectedRows)},
12.样式
<div class="sponeli"><span>维保到期前</span><a-input v-model="queryData.maintenanceExpire" /><span>天进行预警提醒</span></div><div class="sponeli"><span>租赁到期前</span><a-input v-model="queryData.rentExpire" /><span>天进行预警提醒</span></div><div class="sponeli"><span>闲置天数超过</span><a-input v-model="queryData.idleLimit" /><span>天进行预警提醒</span></div><div class="sponeli"><span>经营资产闲置率高于</span><a-input v-model="queryData.idleRateMaxLimit" /><span>进行预警提醒</span></div><div class="sponeli"><span>经营资产收租率低于</span><a-input v-model="queryData.rentRateMinLimit" /><span>进行预警提醒</span></div>
13.附件上传
<a-form-itemlabel="附件:":labelCol="{ span: 2 }":wrapperCol="{ span: 21 }"><a-uploadname="file":multiple="true":action="HttpAction":headers="headers"@change="handleChange":fileList="fileList"><a-button> <a-icon type="upload" />上传</a-button></a-upload>
HttpAction: `${process.env.VUE_APP_API_FATHER_URL}/upms/file/upload?fileType=image&dir=material/`, // 附件上传请求连接
headers: {// 请求头部Authorization: Cookie.get("Authorization"),},
import Cookie from "js-cookie";
// 文件上传handleChange(info) {if (info.file.status !== "uploading") {console.log(info.file, info.fileList);}if (info.file.status === "done") {// this.$message.success(`${info.file.name} file uploaded successfully`);console.log(info.file);} else if (info.file.status === "error") {this.$message.error(`${info.file.name} file upload failed.`);}let fileList = [...info.fileList];// fileList = fileList.slice(-1);fileList = fileList.map((file) => {if (file.response) {// Component will show file.url as linkfile.url = file.response.data;}return file;});this.fileList = fileList;},
fileList:[]
详情的时候
setFile(record) {let row = record;this.fileList = [];if (row.fileUrl && row.fileName) {row.fileUrl.split(",").forEach((item, index) => {row.fileName.split(",").forEach((item2, index2) => {if (index == index2) {let imgUrl = {uid: randomLenNum(),status: "done",name: item2,response: {link: item,},url: item,};this.fileList.push(imgUrl);console.log(this.fileList);}});});}},