1、实现效果
1.1、文件目录
1.2、说明
1、本组件支持列表的表头自定义配置,checkbox实现
2、本组件支持列表列排序,vuedraggable是拖拽插件,上图中字段管理里的拖拽效果 ,需要的话请自行npm install
3、本组件支持查询条件动态配置,穿梭框实现
2、index.vue(最父级,调用入口)
<template><div class="allCustomer-container"><zn-query-form><zn-query-form-top-panel><!-- 字段管理 --><field-management @setColumns="getColumns" :options="options" /><el-popover placement="right" width="650" style="margin-right: 20px" trigger="click"><el-transfer v-model="transfers" :props="{ key: 'name', label: 'label'}" :titles="['待选择', '已选择']" @change="rightcheckchange":data="options" /><el-button type="text" size="mini" slot="reference" style="border: none;font-size: 14px;margin-left: 10px; " ><i class="el-icon-s-tools">查询配置</i></el-button></el-popover><!-- 搜索 --><search @up-Search="upSearch" :options="requestParams" /><!-- 筛选条件 --><!-- <filter-tag :tagList="conditionList" @up-table="tableUpdate" /> --><div class="filter-tag ml-10 mr-10"><el-tag @close="conditionClose(index)" style="margin-left: 10px" v-for="(tag, index) in conditionList" :key="index" closable:type="tag.prop"><span>{{ tag.tagLabel }} :</span><span style="color: red">{{ tag.tagValue }}</span></el-tag></div></zn-query-form-top-panel></zn-query-form><zn-filter-table ref="filterTable" :multiple="true" :tableData="tableList" :finallyColumns="finallyColumns" :deatilsPath="deatilsPath"@selectList="getSelect" @fetch-data="fetchData" :key="datekey" /><zn-pagination v-show="total > 0" :page.sync="queryForm.page" :limit.sync="queryForm.listRows" @pagination="fetchData" :total="total":algin="'right'" /></div>
</template><script>import FieldManagement from './components/FieldManagement'import ZnQueryForm from './components/ZnQueryForm'import ZnQueryFormTopPanel from './components/ZnQueryFormTopPanel'import Search from './components/Search'import ZnPagination from './components/ZnPagination'import ZnFilterTable from './components/ZnFilterTable'export default {name: 'allCustomer',components: {FieldManagement,ZnQueryForm,ZnQueryFormTopPanel,Search,ZnPagination,ZnFilterTable},provide() {return {mark: 'Member', //特定标识,根据业务模块不同,传输的标识也不同,标识由后端定义(或者字典维护)}},data() {return {transfers: [],requestParams: [],total: 0,tableList: [],listLoading: false,queryForm: {page: 1,listRows: 10,keywords: '',_filter: {}, //头部筛选},deatilsPath: '/customer/deatils', //表格当前行跳转路径options: [],conditionList: [], //自定义筛选条件columns: [], //筛选条件中的数据checkList: [], //筛选条件中选中的数据multipleList: [], //表格复选多选中的数据datekey: Date.now(),options: [{id: 0,name: 'name',label: '姓名',type: 'text',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 1,name: 'age',label: '年龄',type: 'number_range',param: '',isShow: true,exp: 'age',extra: {icon: 'file-list-line',},},{id: 2,name: 'phone_main',label: '主要电话',type: 'text',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',columnStyle: 'labelCall',},},{id: 3,name: 'phone_backup',label: '备用电话',type: '',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 4,name: 'id_card',label: '身份证号',type: '',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 5,name: 'birth_day',label: '生日',type: 'date_range',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 6,name: 'sex',label: '性别',type: 'select',param: [{label: '未知',value: 0,},{label: '男',value: 1,},{label: '女',value: 2,},],isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 7,name: 'type',label: '老人类型',type: 'select',param: [{value: 101,label: '独居',},{value: 102,label: '孤老',},{value: 103,label: '失独',},{value: 104,label: '其他',},],isShow: true,exp: '',extra: {icon: 'file-list-line',columnStyle: 'labelTags',labelTags: {101: 'table-status-blue',102: 'table-status-brown',103: 'table-status-green',104: 'table-status-yellow',},},},{id: 8,name: 'street',label: '街道',type: 'text',param: '',isShow: true,exp: 'street',extra: {icon: 'file-list-line',},},{id: 9,name: 'committee',label: '居委',type: 'text',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 10,name: 'address',label: '详细地址',type: 'text',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 11,name: 'reg_address',label: '户籍地址',type: 'text',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 12,name: 'source',label: '来源',type: 'select',param: [],isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 13,name: 'create_time',label: '创建时间',type: 'datetime_range',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 14,name: 'update_time',label: '更新时间',type: 'datetime_range',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 15,name: 'create_user_id',label: '创建人',type: '',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},{id: 16,name: 'update_user_id',label: '更新人',type: '',param: '',isShow: true,exp: '',extra: {icon: 'file-list-line',},},],}},computed: {finallyColumns() {let list = this.columns.filter((item) => this.checkList.includes(item.label))console.log(list)//这里更新了datekey ,组件就会刷新this.datekey = Date.now()return list},},watch: {},created() {this.fetchData()},mounted() { },methods: {rightcheckchange() {this.requestParams = [];for (let i = 0; i < this.transfers.length; i++) {for (let j = 0; j < this.options.length; j++) {if (this.transfers[i] === this.options[j].name) {this.requestParams.push(this.options[j])}}}},conditionClose(index) {this.conditionList.splice(index, 1) // 关闭条件tag-发射给父组件 删除已选中的},// 请求table数据async fetchData(arr) {this.listLoading = true//根据后端要求格式特殊处理if (Array.isArray(arr) && arr.length > 0) {this.conditionList = arr //筛选tag赋值//调用tag组件this.queryForm._filter = {} //每次进来置空arr.forEach((item) => {this.queryForm._filter[item['fieldName']] = item['value']})}this.tableList = [{id: 1,type: {value: 101,label: '独居',},name: '柳倩倩',marital_status: {value: 0,label: '--',},phone_main: '0215567252',phone_backup: '18221274181',id_card: '350583199202224336',birth_day: '1976-03-02',tags: ['123124'],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117501',label: '松江工业区',},committee: {value: '310117501498',label: '松江工业区虚拟社区',},address: '泰晤士小镇',reg_address: '户籍',remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 10:52:45',update_time: '2022-04-06 18:28:28',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 46,},{id: 2,type: {value: 102,label: '孤老',},name: '潘宗磊',marital_status: {value: 0,label: '--',},phone_main: '13822223333',phone_backup: '18817673315',id_card: '34240119881214323X',birth_day: '1988-12-14',tags: ['老人', '身体不便', '高血压'],sex: {value: 2,label: '女',},province: {value: '34',label: '安徽省',},city: {value: '3415',label: '六安市',},area: {value: '341503',label: '裕安区',},street: {value: '310117006',label: '九里亭街道',},committee: {value: '341503105205',label: '泉水村委会',},address: '泉水冲',reg_address: '上海市松江区泰晤士小镇',remark: '备注111',status: 1,general_info: '',medical_card: '123123',blood_type: {value: 'AB',label: 'AB',},health_info: '',physical_condition:'<p><font color="#c24f4a"><b><i>正常</i></b></font><span style="font-size: 14px;"></span></p>',basic_info: '<p>11</p>',allergic_drugs: '<p>22</p>',medical_records: '<p>33</p>',is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 11:01:06',update_time: '2022-04-09 15:38:23',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: ['2', '3'],frequency: {value: 1,label: '一周两次',},service_start: '2022-04-20',service_end: '2022-04-30',service_status: {value: 2,label: '取消',},service_cancel_reason: {value: 3,label: '搬迁',},agent_id: {value: 1,label: 'admin',},age: 33,},{id: 3,type: {value: 103,label: '失独',},name: '宋岩',marital_status: {value: 0,label: '--',},phone_main: '',phone_backup: '13917303249',id_card: '350583199202224336',birth_day: '2022-01-13',tags: [],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117001',label: '岳阳街道',},committee: {value: '310117001002',label: '醉白池社区居委会',},address: '新凯城',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 11:42:19',update_time: '2022-02-15 15:07:14',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 0,},{id: 4,type: {value: 104,label: '其他',},name: '李三',marital_status: {value: 0,label: '--',},phone_main: '',phone_backup: '13917303249',id_card: '350583199202224336',birth_day: '2022-01-04',tags: [],sex: {value: 1,label: '男',},province: {value: '12',label: '天津市',},city: {value: '1201',label: '市辖区',},area: {value: '120103',label: '河西区',},street: {value: '120103002',label: '下瓦房街道',},committee: {value: '120103002003',label: '台北路社区居委会',},address: '发达',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: '',label: ' -- ',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 12:03:59',update_time: '2022-02-18 17:01:12',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 0,},{id: 5,type: {value: 102,label: '孤老',},name: '张三',marital_status: {value: 0,label: '--',},phone_main: '',phone_backup: '18221274181',id_card: '51382219941101899X',birth_day: '1994-11-30',tags: [],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117004',label: '中山街道',},committee: {value: '310117004014',label: '中山苑社区居委会',},address: '泰晤士小镇卡纳比岛',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'O',label: 'O',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 12:05:45',update_time: '2022-01-25 17:34:37',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 27,},{id: 6,type: {value: 102,label: '孤老',},name: '陈情期',marital_status: {value: 0,label: '--',},phone_main: '0215567252',phone_backup: '18817673315',id_card: '350583199202224336',birth_day: '2022-01-05',tags: [],sex: {value: 2,label: '女',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117501',label: '松江工业区',},committee: {value: '310117501498',label: '松江工业区虚拟社区',},address: '新凯城',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 14:44:58',update_time: '2022-02-17 14:39:08',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 0,},{id: 7,type: {value: 102,label: '孤老',},name: '李斯',marital_status: {value: 0,label: '--',},phone_main: '0215567252',phone_backup: '18221274180',id_card: '51382219941101899X',birth_day: '2019-01-18',tags: [],sex: {value: 0,label: '未知',},province: {value: '51',label: '四川省',},city: {value: '5101',label: '成都市',},area: {value: '510104',label: '锦江区',},street: {value: '510104017',label: '锦官驿街道',},committee: {value: '510104017001',label: '水井坊社区居委会',},address: '卡纳比岛',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '良好',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 15:14:29',update_time: '2022-02-19 17:32:08',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 3,},{id: 8,type: {value: 102,label: '孤老',},name: '李请',marital_status: {value: 0,label: '--',},phone_main: '0215567252',phone_backup: '18221274181',id_card: '350583199202224336',birth_day: '2022-01-04',tags: [],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117501',label: '松江工业区',},committee: {value: '310117501498',label: '松江工业区虚拟社区',},address: '泰晤士小镇',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 15:42:02',update_time: '2022-01-25 17:33:23',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 0,},{id: 9,type: {value: 102,label: '孤老',},name: '柳慢慢',marital_status: {value: 0,label: '--',},phone_main: '0215567252',phone_backup: '18221274182',id_card: '350583199202224336',birth_day: '1945-06-05',tags: [],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117001',label: '岳阳街道',},committee: {value: '310117001001',label: '龙潭社区居委会',},address: '新凯城',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'A',label: 'A',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 19:58:02',update_time: '2022-02-16 11:18:17',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 76,},{id: 10,type: {value: 102,label: '孤老',},name: '小千',marital_status: {value: 0,label: '--',},phone_main: '',phone_backup: '18221274181',id_card: '350583199202224336',birth_day: '2022-01-03',tags: [],sex: {value: 1,label: '男',},province: {value: '31',label: '上海市',},city: {value: '3101',label: '市辖区',},area: {value: '310117',label: '松江区',},street: {value: '310117001',label: '岳阳街道',},committee: {value: '310117001001',label: '龙潭社区居委会',},address: '新凯城',reg_address: null,remark: '',status: 1,general_info: '',medical_card: null,blood_type: {value: 'B',label: 'B',},health_info: '',physical_condition: null,basic_info: null,allergic_drugs: null,medical_records: null,is_contract: 0,contract_imgs: [],source: {value: '',label: ' -- ',},create_time: '2022-01-18 20:00:32',update_time: '2022-01-25 17:33:03',create_user_id: 0,update_user_id: 0,contract: {value: 1,label: '幸福久久',},category: [],frequency: {value: '',label: ' -- ',},service_start: null,service_end: null,service_status: {value: 1,label: '正常',},service_cancel_reason: {value: 0,label: '--',},agent_id: {value: 1,label: 'admin',},age: 0,},],this.total = this.tableList.lengththis.listLoading = false},// 更新搜索字段,更新table数据upSearch(val) {console.log("父页面收到查询条件")this.queryForm = valconsole.log(this.queryForm)this.fetchData()},// 获取多选选中的table数据(需求未出,功能暂留)getSelect(list) {this.multipleList = listalert('list', list)},// 子组件筛选条件返回getColumns(columns, checkList) {this.columns = columnsthis.checkList = checkList},// 自定义检索(popover组件(data为对象)和tag组件(data为数组))发射出来的事件tableUpdate(data) {this.$refs.filterTable.tableUpdate(data)},},}
</script><style lang="scss" scoped>.el-button {border: none;margin-bottom: 0;}::v-deep.pop-li {.el-button {color: black !important;}}
</style>
3、FieldManagement组件
<template><el-dropdown class="ml-10 mr-10" trigger="hover"><el-button type="text" size="medium" ><i class="el-icon-tickets">字段管理</i></el-button><el-dropdown-menu slot="dropdown"><el-row :gutter="10" type="flex" class="row-flex"><el-col :span="6"><el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">勾选您要选择的字段</el-checkbox></el-col><el-divider /><el-checkbox-group v-if="options.length > 0" v-model="checkList" @change="handleCheckedChange"><zn-draggable v-bind="dragOptions" :list="options" class="zn-draggable" v-model="options" @sort="dragSort"><el-col :span="24" class="checkbox-group-col" v-for="item in options" :key="item.id"><!-- <zn-icon icon="drag-drop-line" /> --><el-checkbox :label="item.label">{{ item.label }}</el-checkbox></el-col></zn-draggable></el-checkbox-group></el-row></el-dropdown-menu></el-dropdown>
</template><script>import ZnDraggable from 'vuedraggable'export default {name: 'fieldManagement',components: {ZnDraggable,},inject: ['mark'],props: {options: {type: Array, // table数据default: () => [],},},data() {return {checkAll: false,checkList: [],options: [],isIndeterminate: true,}},computed: {dragOptions() {return {animation: 600,group: 'description',}},},watch: {},created() {this.getHeader()},mounted() { },methods: {dragSort(e) {// e.newIndex 为draggable插件给的值this.options[e.newIndex].sort = e.newIndex + 1;// 重新排序this.options.map((item, index) => {item.sort = index + 1;})//对checkList进行排序let sortCheck = [];this.options.map((item) => {if (this.checkList.includes(item.label)) {sortCheck.push(item)}})let checks = []sortCheck.map((item) => {checks.push(item.label)})this.$emit('setColumns', this.options, checks)},// 字段管理的接口是统一的 , 只有业务模块的mark标识不同 , 所以请求就写在了组件里async getHeader() {this.options.map((item) => {if (item.isShow == true) {this.checkList.push(item.label)}})this.$emit('setColumns', this.options, this.checkList)},// 操纵全选handleCheckAllChange(val) {if (val) {this.options.map((item) => {this.checkList.push(item.label)})} else {this.checkList = []}this.isIndeterminate = false// 向父组件发送数据this.$emit('setColumns', this.options, this.checkList)},// 单个数据选中handleCheckedChange(value) {let checkedCount = value.lengththis.checkAll = checkedCount === this.options.lengththis.isIndeterminate =checkedCount > 0 && checkedCount < this.options.length// 向父组件发送数据this.$emit('setColumns', this.options, this.checkList)},},}
</script><style lang="scss" scoped>.row-flex {padding: 15px;display: flex;flex-direction: column;}.zn-draggable {display: flex;flex-direction: column;justify-content: flex-start;}.checkbox-group-col {max-height: 100px;overflow-y: auto;.el-checkbox {padding: 3px 0;width: 100%;margin-right: 0;}}
</style>
4、ZnQueryForm组件
<template><el-row class="zn-query-form" :gutter="0"><slot /></el-row></template><script>export default {name: 'ZnQueryForm',}</script><style lang="scss" scoped>@mixin panel {display: flex;flex-wrap: wrap;align-content: center;align-items: center;justify-content: flex-start;min-height: 20px;margin: 0 0 0 0;> .el-button {margin: 0 10px 0 0 !important;}}.zn-query-form {::v-deep {.el-form-item:first-child {margin: 0 0 0 0 !important;}.el-form-item + .el-form-item {margin: 0 0 0 0 !important;.el-button {margin: 0 0 0 10px !important;}}.top-panel {@include panel;}.bottom-panel {@include panel;border-top: 1px solid #dcdfe6;}.left-panel {@include panel;}.right-panel {@include panel;justify-content: flex-end;}}}</style>
5、ZnQueryFormTopPanel组件
<template><el-col :span="24"><div class="top-panel"><slot /></div></el-col></template><script>export default {name: 'ZnQueryFormTopPanel',}</script>
6、Search组件
<template><div><el-form :model="queryParams" ref="queryParams" :inline="true" style="margin-bottom: 10px;"><el-form-item v-for="item in options" :key="item.id" :prop="item.name" style="margin-right: 10px !important"><el-select v-if="item.type === 'select'" v-model="queryParams[item.name]" :placeholder="'请选择'+item.label" @change="handleQuery"clearable filterable><el-option v-for="dict in item.param" :key="dict.value" :label="dict.label" :value="dict.value" /></el-select><!-- date 单个日期--><el-date-picker v-else-if="item.type == 'date'" v-model="queryParams[item.name]" type="date" clearable placeholder="选择日期"value-format="yyyy-MM-dd" @change="handleQuery" /><!-- datetime 日期时间--><el-date-picker v-else-if="item.type == 'datetime'" v-model="queryParams[item.name]" type="datetime" placeholder="选择日期时间"value-format="yyyy-MM-dd HH:mm:ss" @change="handleQuery"></el-date-picker><!-- date_range 日期范围--><el-date-picker v-else-if="item.type == 'date_range'" v-model="queryParams[item.name]" type="daterange" range-separator="至"start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" @change="handleQuery"></el-date-picker><!-- datetime_range 日期时分秒范围--><el-date-picker v-else-if="item.type == 'datetime_range'" v-model="queryParams[item.name]" clearable type="datetimerange"range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss" @change="handleQuery"></el-date-picker><el-input v-else v-model="queryParams[item.name]" clearable :placeholder="'请输入'+item.label" @clear="handleQuery"></el-input></el-form-item><el-form-item style="right: 20px;"><el-button type="primary" size="mini" @click="handleQuery">查询</el-button></el-form-item></el-form></div>
</template><script>export default {name: 'Search',components: {},props: {options: {type: Array, // table数据default: () => [],},},data() {return {isActive: false,searchText: '',queryParams: {},}},computed: {},watch: {},created() { },mounted() { },methods: {handleQuery() {console.log(this.queryParams)this.$emit('up-Search', this.queryParams) //改变搜索字段的value},},}
</script><style lang="scss" scoped>.public-search {display: inline-block;overflow: hidden;cursor: pointer;border: 1px solid white;}::v-deep.isActive {border: 1px solid red;transition: all 0.3s ease-in-out;}
</style>
7、ZnPagination组件
<template><div :class="{ hidden: hidden }" class="pagination-container"><el-pagination:style="{'text-align':algin}":background="background":current-page.sync="currentPage":page-size.sync="pageSize":layout="layout":page-sizes="pageSizes":total="total"v-bind="$attrs"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></div></template><script>import { scrollTo } from './utils/scroll-to'export default {name: 'ZnPagination',props: {total: {required: true,type: Number,},page: {type: Number,default: 1,},limit: {type: Number,default: 10,},pageSizes: {type: Array,default() {return [10, 20, 30, 50]},},layout: {type: String,default: 'total, sizes, prev, pager, next, jumper',},background: {type: Boolean,default: true,},autoScroll: {type: Boolean,default: true,},hidden: {type: Boolean,default: false,},algin: {type: String,default: ()=>'center',},},computed: {currentPage: {get() {return this.page},set(val) {this.$emit('update:page', val)},},pageSize: {get() {return this.limit},set(val) {this.$emit('update:limit', val)},},},methods: {handleSizeChange(val) {this.$emit('pagination', { page: this.currentPage, limit: val })if (this.autoScroll) {scrollTo(0, 800)}},handleCurrentChange(val) {this.$emit('pagination', { page: val, limit: this.pageSize })if (this.autoScroll) {scrollTo(0, 800)}},},}</script><style lang="scss" scoped>.pagination-container {background: #fff;}.pagination-container.hidden {display: none;}</style>
8、ZnFilterTable组件
<template><div class="filter-table pt-10 pb-10"><!-- 表格 --><el-tableref="table":data="tableData"style="width: 100%"borderstripe@selection-change="handleSelectionChange"@row-click="toDeatils":header-cell-style="{'text-align':'center'}"><el-table-columntype="selection"width="55"align="center"v-if="multiple"></el-table-column><el-table-columnv-for="(item, index) in finallyColumns":key="item.id":label="item.label":align="item.type === 'number_range'?'right':item.type === 'select'?'center':'left'":prop="item.name"min-width="130"show-overflow-tooltip><template slot="header"><type-popover:columnIndex="index":column="item":filterOptions="item.param"@tableUpdate="tableUpdate"></type-popover></template><template #default="{ row }"><!-- 每一列涉及value值判断显示label ,以及状态颜色 --><spanv-if="item.extra.columnStyle == 'labelTags'":class="item.extra.labelTags[row.type.value]">{{ row.type.label }}</span><!-- 电话后面有电话图标 --><spanv-else-if="item.extra.columnStyle == 'labelCall' && row[item.name] != ''">{{ row[item.name] }}<zn-icon class="column-label-call" icon="phone-fill" @click.stop="makeACall"/></span><!-- 性别、街道、居委、数据是对象{value:"",label:""} --><span v-else-if="['sex','street','committee','source'].includes(item.name)">{{ row[item.name].label }}</span><span v-else>{{ row[item.name] }}</span></template></el-table-column><!-- 如有操作列 ↓--><el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200"><template slot-scope="scope"><el-button size="mini" type="text" @click="handleSelect(scope.row)" >详情</el-button></template></el-table-column></el-table></div></template><script>import TypePopover from './TypePopover'import ZnIcon from './ZnIcon'export default {name: 'ZnFilterTable',components: { TypePopover,ZnIcon },props: {tableData: {type: Array, // table数据default: () => [],},finallyColumns: {type: Array, // table数据default: () => [],},multiple: {type: Boolean, // table数据default: () => false,},deatilsPath: {type: String, // table数据default: () => '',},},data() {return {conditionList: [],multipleSelection: [], //table多选数据}},computed: {},mounted() {},methods: {handleSelectionChange(val) {this.multipleSelection = valthis.$emit('selectList', this.multipleSelection)},handleSelect(item){console.log(item)alert("详情!")},// 自定义检索(popover组件(data为对象)和tag组件(data为数组))发射出来的事件tableUpdate(data) {console.log(data)let flag = true// 筛选条件如果已经存在,就更新,注意判别传递过来的数据类型// arr.constructor === Arrayif (Array.isArray(data)) {this.conditionList = JSON.parse(JSON.stringify(data))this.conditionList.forEach((item, index) => {if (item.fieldName == data.fieldName) {item.value = data.valueflag = false}})} else if (data.fieldName) {this.conditionList.push(data) // 如果没有就添加}console.log(this.conditionList)this.$parent.fetchData(this.conditionList)},toDeatils(row) {if (this.deatilsPath) {this.$router.push({ path: this.deatilsPath, query: row.id })} else {this.$baseMessage('请配置所需要跳转的路径','warning','zn-hey-message-warning')}},// 拨打电话makeACall(){},},}</script><style scoped lang='scss'>/* // 占位,解决点击自己写的自定义筛选 会冒泡到排序 */::v-deep .el-table__column-filter-trigger {display: none !important;}::v-deep.filter-table {/* // table状态标签颜色定义 */[class*='table-status'] {display: inline-block;border-radius: 2px;padding: 0px 12px;}/* // 蓝色 */[class*='table-status-blue'] {background: #e6effb;color: #005bd9;}/* // 棕色 */[class*='table-status-brown'] {background: #fff6ec;color: #ffa336;}/* // 绿色 */[class*='table-status-green'] {background: #e8fff0;color: #00b47e;}/* // 黄色 */[class*='table-status-yellow'] {background: #fffae8;color: #f9c200;}/* // 粉色 */[class*='table-status-pink'] {background: #ffece8;color: #f53f3f;}/* // 白色 */[class*='table-status-white'] {background: #f2f3f5;color: #1d2129;}}</style>
9、TypePopover组件
<template><!-- 每个table表头的popover --><!-- 注意:逻辑部分尽量不好写到这个组件内,因为这个组件是根据外面table循环创建的,在这里写逻辑会非常影响性能 --><div class="customHeader" style="display: inline-block"><el-popoverplacement="bottom"trigger="click":ref="`popover-${columnIndex}`"><!-- table表头文字显示--><span slot="reference" class="label"><zn-icon :icon="column.extra.icon" />{{ column.label }} <i class="el-icon-arrow-down"></i></span><!-- text 文本 --><div v-if="column.type == 'text'"><el-inputclearablev-model.trim="filterForm.value"placeholder="请输入查询内容"@keyup.native.enter="confirm()"></el-input></div><!-- number 数字框 --><div v-else-if="column.type == 'number'"><el-inputclearableoninput="value=value.replace(/[^0-9.]/g,'')"v-model.trim="filterForm.value"placeholder="请输入数字"@keyup.native.enter="confirm()"></el-input></div><!-- number_range 数字范围--><div v-else-if="column.type == 'number_range'"><el-inputstyle="width: 120px"clearableoninput="value=value.replace(/[^0-9.]/g,'')"v-model.trim="filterForm.value"placeholder="请输入数字"></el-input>-<el-inputstyle="width: 120px"clearableoninput="value=value.replace(/[^0-9.]/g,'')"v-model.trim="spareValue"placeholder="请输入数字"></el-input></div><!-- date 单个日期--><div v-else-if="column.type == 'date'"><el-date-pickerv-model="filterForm.value"type="date"clearableplaceholder="选择日期"value-format="yyyy-MM-dd"/></div><!-- datetime 日期时间--><div v-else-if="column.type == 'datetime'"><el-date-pickerv-model="filterForm.value"type="datetime"placeholder="选择日期时间"value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker></div><!-- date_range 日期范围--><div v-else-if="column.type == 'date_range'"><el-date-pickerv-model="filterForm.value"type="daterange"range-separator="至"start-placeholder="开始日期"end-placeholder="结束日期"value-format="yyyy-MM-dd"></el-date-picker></div><!-- datetime_range 日期时分秒范围--><div v-else-if="column.type == 'datetime_range'"><el-date-pickerv-model="filterForm.value"clearabletype="datetimerange"range-separator="至"start-placeholder="开始日期"end-placeholder="结束日期"value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker></div><!-- select 下拉选择--><div v-else-if="column.type == 'select'"><el-selectv-model="filterForm.value"placeholder="请选择"style="width: 100%"clearable><el-optionv-for="(item, index) in filterOptions":key="index":label="item.label":value="item.value"></el-option></el-select></div><!-- radio 单选--><div v-else-if="column.type == 'radio'"><el-radio-group v-model="filterForm.value"><el-radiov-for="(item, index) in filterOptions":key="index":label="item.value":value="item.value">{{ item.label }}</el-radio></el-radio-group></div><!-- checkBox 多选--><div v-else-if="column.type == 'checkBox'"><el-checkbox-group v-model="checkboxList"><el-checkboxv-for="(item, index) in filterOptions":key="index":label="item.value":value="item.value">{{ item.label }}</el-checkbox></el-checkbox-group></div><!-- confirm 确定框--><div style="text-align: right"><el-button@click="confirm()"type="primary"size="mini"class="confirm">确定</el-button></div></el-popover></div></template><script>import ZnIcon from './ZnIcon'export default {name: 'typePopover',components: {ZnIcon},// column 当前列数据,filterOptions 多选/单选/下拉/数据props: ['column', 'filterOptions', 'columnIndex'],data() {return {filterForm: {tagLabel: this.column.label, //筛选tag label(tag用)tagValue: '', //筛选tag value(tag用)value: '', //所筛选的数据(后端接收用)fieldName: this.column.name, //当前表头字段(后端接收用)},spareValue: '', //备用Value popover里如是两个值的话需要用此来拼接checkboxList: [],}},created() {},methods: {confirm() {let minValue = this.filterForm.value //数值双向绑定 做个闭环赋值let type = this.column.type// 跟后端商定 , 多个值存在时进行判断 , 以filterForm.value一个值为字符串的形式传递// 根据需求做了处理// checkBox和radio和select由于value值的原因需要处理if (type == 'checkBox' || type == 'radio' || type == 'select') {if (type == 'checkBox') {this.filterForm.value = this.checkboxList.join(',')}if (this.column.param && this.column.param.length > 0) {let str = ''this.column.param.forEach((i, t) => {if (type == 'checkBox' && i.value == Number(this.checkboxList[t])) {str = str + i.label}if (type == 'radio' && i.value == Number(this.filterForm.value)) {str = str + i.label}if (type == 'select' && i.value == Number(this.filterForm.value)) {str = str + i.label}})this.filterForm.tagValue = str}}// 数字范围else if (type == 'number_range') {this.filterForm.tagValue =this.filterForm.value + ' - ' + this.spareValuethis.filterForm.value = this.filterForm.value + ',' + this.spareValue} else if (this.filterForm.value == '' && !this.spareValue) {return this.$message.warning('请输入或选择筛选条件')} else {this.filterForm.tagValue = this.filterForm.value //其他类型的赋值给tag用}this.$emit('tableUpdate', this.filterForm) //传递的是对象this.filterForm.value = minValue //数值双向绑定 做个闭环赋值 ,俗称瞒天过海this.$refs['popover-' + this.columnIndex].doClose() // 关闭popover},},}</script><style scoped>.confirm {margin-top: 10px;}/* 禁止双击选中文字 */.label {-moz-user-select: none; /*火狐*/-webkit-user-select: none !important; /*webkit浏览器*/-ms-user-select: none; /*IE10*/-khtml-user-select: none; /*早期浏览器*/user-select: none;}.labelColor {color: #409eff;}.el-icon-arrow-down {cursor: pointer;}.el-checkbox-group {display: flex;justify-content: flex-start;flex-direction: column;max-height: 150px;overflow-y: auto;}</style>
10、ZnIcon组件
<template><divv-if="isExternal":style="styleExternalIcon"class="svg-external-icon zn-icon"v-on="$listeners"/><svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"><use :xlink:href="iconClass" /></svg>
</template><script>
import { isExternal } from '@/utils/validate'export default {name: 'ZnSvgIcon',props: {iconName: {type: String,default: '',required: true,},className: {type: String,default: '',},},computed: {isExternal() {return isExternal(this.iconName)},iconClass() {return `#icon-${this.iconName}`},svgClass() {if (this.className) {return 'zn-icon ' + this.className} else {return 'zn-icon'}},styleExternalIcon() {return {mask: `url(${this.iconName}) no-repeat 50% 50%`,'-webkit-mask': `url(${this.iconName}) no-repeat 50% 50%`,}},},
}
</script><style scoped>
.zn-icon {width: 1rem;height: 1rem;fill: currentColor;overflow: hidden;font-size: 16px;text-align: center;vertical-align: -3px;cursor: pointer;margin-right: 5px;
}
/* .el-button .zn-icon {width: 1rem;height: 1rem;vertical-align: -2.5px;color: inherit;
} */
.zn-icon:hover path {fill: var(--mainTone);
}.svg-external-icon {background-color: currentColor;mask-size: cover !important;display: inline-block;
}
</style>
11、utils
11.1、encrypt.js
import JSEncrypt from 'jsencrypt'
import { getPublicKey } from '@/api/publicKey'const privateKey ='MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMFPa+v52FkSUXvcUnrGI/XzW3EpZRI0s9BCWJ3oNQmEYA5luWW5p8h0uadTIoTyYweFPdH4hveyxlwmS7oefvbIdiP+o+QIYW/R4Wjsb4Yl8MhR4PJqUE3RCy6IT9fM8ckG4kN9ECs6Ja8fQFc6/mSl5dJczzJO3k1rWMBhKJD/AgMBAAECgYEAucMakH9dWeryhrYoRHcXo4giPVJsH9ypVt4KzmOQY/7jV7KFQK3x//27UoHfUCak51sxFw9ek7UmTPM4HjikA9LkYeE7S381b4QRvFuf3L6IbMP3ywJnJ8pPr2l5SqQ00W+oKv+w/VmEsyUHr+k4Z+4ik+FheTkVWp566WbqFsECQQDjYaMcaKw3j2Zecl8T6eUe7fdaRMIzp/gcpPMfT/9rDzIQk+7ORvm1NI9AUmFv/FAlfpuAMrdL2n7p9uznWb7RAkEA2aP934kbXg5bdV0R313MrL+7WTK/qdcYxATUbMsMuWWQBoS5irrt80WCZbG48hpocJavLNjbtrjmUX3CuJBmzwJAOJg8uP10n/+ZQzjEYXh+BszEHDuw+pp8LuT/fnOy5zrJA0dO0RjpXijO3vuiNPVgHXT9z1LQPJkNrb5ACPVVgQJBALPeb4uV0bNrJDUb5RB4ghZnIxv18CcaqNIft7vuGCcFBAIPIRTBprR+RuVq+xHDt3sNXdsvom4h49+Hky1b0ksCQBBwUtVaqH6ztCtwUF1j2c/Zcrt5P/uN7IHAd44K0gIJc1+Csr3qPG+G2yoqRM8KVqLI8Z2ZYn9c+AvEE+L9OQY='/*** 最长加密长度* @type {number}*/
const MAX_ENCRYPT_BLOCK = 117
/*** 最长解码长度* @type {number}*/
const MAX_DECRYPT_BLOCK = 128/*** @description RSA加密(支持长字符加密)* @param data* @returns {Promise<{param: PromiseLike<ArrayBuffer>}|*>}*/
export async function encryptedData(data) {let publicKeyconst res = await getPublicKey()publicKey = res.data.publicKeyif (res.data.mockServer) {publicKey = ''}if (publicKey === '') {return data}const encrypt = new JSEncrypt()encrypt.setPublicKey(`-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----`)let bufTmp = ''let hexTmp = ''let result = ''const buffer = Buffer.from(JSON.stringify(data))let offSet = 0const inputLen = buffer.lengthwhile (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {bufTmp = buffer.slice(offSet, offSet + MAX_ENCRYPT_BLOCK)} else {bufTmp = buffer.slice(offSet, inputLen)}hexTmp = encrypt.encrypt(bufTmp.toString())result += atob(hexTmp)offSet += MAX_ENCRYPT_BLOCK}return btoa(result)
}/*** @description RSA解密(支持长字符解密)* @param data* @returns {PromiseLike<ArrayBuffer>}*/
export function decryptedData(data) {const decrypt = new JSEncrypt()decrypt.setPrivateKey(`-----BEGIN RSA PRIVATE KEY-----${privateKey}-----END RSA PRIVATE KEY-----`)let bufTmp = ''let hexTmp = ''let result = ''const buffer = atob(data)let offSet = 0const inputLen = buffer.lengthwhile (inputLen - offSet > 0) {if (inputLen - offSet > MAX_DECRYPT_BLOCK) {bufTmp = buffer.slice(offSet, offSet + MAX_DECRYPT_BLOCK)} else {bufTmp = buffer.slice(offSet, inputLen)}hexTmp = decrypt.decrypt(btoa(bufTmp))result += hexTmpoffSet += MAX_DECRYPT_BLOCK}return JSON.parse(result)
}
11.2、index.js
/*** @description 格式化时间* @param time* @param cFormat* @returns {string|null}*/
export function parseTime(time, cFormat) {if (arguments.length === 0) {return null}const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'let dateif (typeof time === 'object') {date = time} else {if (typeof time === 'string' && /^[0-9]+$/.test(time)) {time = parseInt(time)}if (typeof time === 'number' && time.toString().length === 10) {time = time * 1000}date = new Date(time)}const formatObj = {y: date.getFullYear(),m: date.getMonth() + 1,d: date.getDate(),h: date.getHours(),i: date.getMinutes(),s: date.getSeconds(),a: date.getDay(),}return format.replace(/{([ymdhisa])+}/g, (result, key) => {let value = formatObj[key]if (key === 'a') {return ['日', '一', '二', '三', '四', '五', '六'][value]}if (result.length > 0 && value < 10) {value = '0' + value}return value || 0})
}/*** @description 格式化时间* @param time* @param option* @returns {string}*/
export function formatTime(time, option) {if (('' + time).length === 10) {time = parseInt(time) * 1000} else {time = +time}const d = new Date(time)const now = Date.now()const diff = (now - d) / 1000if (diff < 30) {return '刚刚'} else if (diff < 3600) {// less 1 hourreturn Math.ceil(diff / 60) + '分钟前'} else if (diff < 3600 * 24) {return Math.ceil(diff / 3600) + '小时前'} else if (diff < 3600 * 24 * 2) {return '1天前'}if (option) {return parseTime(time, option)} else {return (d.getMonth() +1 +'月' +d.getDate() +'日' +d.getHours() +'时' +d.getMinutes() +'分')}
}/*** @description 将url请求参数转为json格式* @param url* @returns {{}|any}*/
export function paramObj(url) {const search = url.split('?')[1]if (!search) {return {}}return JSON.parse('{"' +decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"').replace(/\+/g, ' ') +'"}')
}/*** @description 父子关系的数组转换成树形结构数据* @param data* @returns {*}*/
export function translateDataToTree(data) {const parent = data.filter((value) => value.parentId === 'undefined' || value.parentId === null)const children = data.filter((value) => value.parentId !== 'undefined' && value.parentId !== null)const translator = (parent, children) => {parent.forEach((parent) => {children.forEach((current, index) => {if (current.parentId === parent.id) {const temp = JSON.parse(JSON.stringify(children))temp.splice(index, 1)translator([current], temp)typeof parent.children !== 'undefined' ?parent.children.push(current) :(parent.children = [current])}})})}translator(parent, children)return parent
}/*** @description 树形结构数据转换成父子关系的数组* @param data* @returns {[]}*/
export function translateTreeToData(data) {const result = []data.forEach((item) => {const loop = (data) => {result.push({id: data.id,name: data.name,parentId: data.parentId,})const child = data.childrenif (child) {for (let i = 0; i < child.length; i++) {loop(child[i])}}}loop(item)})return result
}/*** @description 10位时间戳转换年月日时分秒* @param time* @returns {string}*/
export function tenBitTimestamp(time) {const date = new Date(time * 1000)const y = date.getFullYear()let m = date.getMonth() + 1m = m < 10 ? '' + m : mlet d = date.getDate()d = d < 10 ? '' + d : dlet h = date.getHours()h = h < 10 ? '0' + h : hlet minute = date.getMinutes()let second = date.getSeconds()minute = minute < 10 ? '0' + minute : minutesecond = second < 10 ? '0' + second : secondreturn y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合
}
/*** @description 10位时间戳转换时分秒* @param time* @returns {string}*/
export function tenBitTimestampHms(time) {const date = new Date(time * 1000)let h = date.getHours()h = h < 10 ? '0' + h : hlet minute = date.getMinutes()let second = date.getSeconds()minute = minute < 10 ? '0' + minute : minutesecond = second < 10 ? '0' + second : secondreturn h + ':' + minute + ':' + second //组合
}/*** @description 13位时间戳转换* @param time* @returns {string}*/
export function thirteenBitTimestamp(time) {const date = new Date(time / 1)const y = date.getFullYear()let m = date.getMonth() + 1m = m < 10 ? '' + m : mlet d = date.getDate()d = d < 10 ? '' + d : dlet h = date.getHours()h = h < 10 ? '0' + h : hlet minute = date.getMinutes()let second = date.getSeconds()minute = minute < 10 ? '0' + minute : minutesecond = second < 10 ? '0' + second : secondreturn y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合
}/*** @description 获取随机id* @param length* @returns {string}*/
export function uuid(length = 32) {const num = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'let str = ''for (let i = 0; i < length; i++) {str += num.charAt(Math.floor(Math.random() * num.length))}return str
}/*** @description m到n的随机数* @param m* @param n* @returns {number}*/
export function random(m, n) {return Math.floor(Math.random() * (m - n) + n)
}/*** @description addEventListener* @type {function(...[*]=)}*/
export const on = (function () {return function (element, event, handler, useCapture = false) {if (element && event && handler) {element.addEventListener(event, handler, useCapture)}}
})()/*** @description removeEventListener* @type {function(...[*]=)}*/
export const off = (function () {return function (element, event, handler, useCapture = false) {if (element && event) {element.removeEventListener(event, handler, useCapture)}}
})()/*** @description 数组打乱* @param array* @returns {*}*/
export function shuffle(array) {let m = array.length,t,iwhile (m) {i = Math.floor(Math.random() * m--)t = array[m]array[m] = array[i]array[i] = t}return array
}
/*** @description ele表格序号* @param number* @returns {string}*/
export function indexMethod(index) {return index + 1;
}
/*** @description 获取当前时间并打印* @param null* @returns {string}*/
export function getCurrentTime() {var _this = thislet yy = new Date().getFullYear()let mm = new Date().getMonth() + 1let dd = new Date().getDate()let hh = new Date().getHours()let mf =new Date().getMinutes() < 10 ?'0' + new Date().getMinutes() :new Date().getMinutes()let ss =new Date().getSeconds() < 10 ?'0' + new Date().getSeconds() :new Date().getSeconds()return (_this.gettime =yy + '-' + mm + '-' + dd + ' ' + hh + ':' + mf + ':' + ss)
}
/*** @description 数组里对象根据某个值(包含姓名、字母、数字)排序* @param null* @returns {array}*/
export function compare(data) {let chineseChars = [],chars = [],list = [];data.forEach(item => {// 判断是否为中文if (/^[\u4e00-\u9fa5]*$/.test(item.nickname.charAt(0))) {chineseChars.push(item); // 姓名首字符为中文的} else {chars.push(item); // 姓名首字符非中文的(字母,数字)}});chars.sort((a, b) => a.nickname.charCodeAt(0) - b.nickname.charCodeAt(0));chineseChars.sort((a, b) => a.nickname.localeCompare(b.nickname));list = chars.concat(chineseChars); // list为最终想要的数组return list
}
11.3、preventReClick.js
// 防止多次点击,重复请求
import Vue from 'vue'const preventReClick = Vue.directive('preventReClick', {inserted: function (el, binding) {el.addEventListener('click', () => {if (!el.disabled) {el.disabled = truesetTimeout(() => {el.disabled = false}, binding.value || 3000)}})}
});export default{ preventReClick }
11.4、scroll-to.js
Math.easeInOutQuad = function(t, b, c, d) {t /= d / 2if (t < 1) {return c / 2 * t * t + b}t--return -c / 2 * (t * (t - 2) - 1) + b
}// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function() {return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()/*** Because it's so fucking difficult to detect the scrolling element, just move them all* @param {number} amount*/
function move(amount) {document.documentElement.scrollTop = amountdocument.body.parentNode.scrollTop = amountdocument.body.scrollTop = amount
}function position() {return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}/*** @param {number} to* @param {number} duration* @param {Function} callback*/
export function scrollTo(to, duration, callback) {const start = position()const change = to - startconst increment = 20let currentTime = 0duration = (typeof (duration) === 'undefined') ? 500 : durationvar animateScroll = function() {// increment the timecurrentTime += increment// find the value with the quadratic in-out easing functionvar val = Math.easeInOutQuad(currentTime, start, change, duration)// move the document.bodymove(val)// do the animation unless its overif (currentTime < duration) {requestAnimFrame(animateScroll)} else {if (callback && typeof (callback) === 'function') {// the animation is done so lets callbackcallback()}}}animateScroll()
}
11.5、validate.js
/*** @description 判读是否为外链* @param path* @returns {boolean}*/
export function isExternal(path) {return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}/*** @description 校验密码是否小于6位* @param value* @returns {boolean}*/
export function isPassword(value) {return value.length >= 6
}/*** @description 判断是否为数字* @param value* @returns {boolean}*/
export function isNumber(value) {const reg = /^[0-9]*$/return reg.test(value)
}/*** @description 判断是否是名称* @param value* @returns {boolean}*/
export function isName(value) {const reg = /^[\u4e00-\u9fa5]+$/return reg.test(value)
}/*** @description 判断是否为IP* @param ip* @returns {boolean}*/
export function isIP(ip) {const reg =/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/return reg.test(ip)
}/*** @description 判断是否是传统网站* @param url* @returns {boolean}*/
export function isUrl(url) {const reg =/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/return reg.test(url)
}/*** @description 判断是否是小写字母* @param value* @returns {boolean}*/
export function isLowerCase(value) {const reg = /^[a-z]+$/return reg.test(value)
}/*** @description 判断是否是大写字母* @param value* @returns {boolean}*/
export function isUpperCase(value) {const reg = /^[A-Z]+$/return reg.test(value)
}/*** @description 判断是否是大写字母开头* @param value* @returns {boolean}*/
export function isAlphabets(value) {const reg = /^[A-Za-z]+$/return reg.test(value)
}/*** @description 判断是否是字符串* @param value* @returns {boolean}*/
export function isString(value) {return typeof value === 'string' || value instanceof String
}/*** @description 判断是否是数组* @param arg*/
export function isArray(arg) {if (typeof Array.isArray === 'undefined') {return Object.prototype.toString.call(arg) === '[object Array]'}return Array.isArray(arg)
}/*** @description 判断是否是端口号* @param value* @returns {boolean}*/
export function isPort(value) {const reg =/^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/return reg.test(value)
}/*** @description 判断是否是手机号* @param value* @returns {boolean}*/
export function isPhone(value) {const reg = /^1\d{10}$/return reg.test(value)
}/*** @description 判断是否是身份证号(第二代)* @param value* @returns {boolean}*/
export function isIdCard(value) {const reg =/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/return reg.test(value)
}/*** @description 判断是否是邮箱* @param value* @returns {boolean}*/
export function isEmail(value) {const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/return reg.test(value)
}/*** @description 判断是否中文* @param value* @returns {boolean}*/
export function isChina(value) {const reg = /^[\u4E00-\u9FA5]{0,30}$/return reg.test(value)
}/*** @description 判断是否为空* @param value* @returns {boolean}*/
export function isBlank(value) {return (value === null ||false ||value === '' ||value.trim() === '' ||value.toLocaleLowerCase().trim() === 'null')
}/*** @description 判断是否为固话* @param value* @returns {boolean}*/
export function isTel(value) {const reg =/^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})([- ])?)?([0-9]{7,8})(([- 转])*([0-9]{1,4}))?$/return reg.test(value)
}/*** @description 判断是否为数字且最多两位小数* @param value* @returns {boolean}*/
export function isNum(value) {const reg = /^\d+(\.\d{1,2})?$/return reg.test(value)
}/*** @description 判断是否为json* @param value* @returns {boolean}*/
export function isJson(value) {if (typeof value === 'string') {const obj = JSON.parse(value)return !!(typeof obj === 'object' && obj)}return false
}