vue项目开发笔记记录(二)

简介: vue项目开发笔记记录

vue项目开发笔记记录(一):https://developer.aliyun.com/article/1483515


深克隆

/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

数组去重

/**
 * @param {Array} arr
 * @returns {Array}
 */
export function uniqueArr(arr) {
  return Array.from(new Set(arr))
}

创建唯一ID

/**
 * @returns {string}
 */
export function createUniqueString() {
  const timestamp = +new Date() + ''
  const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  return (+(randomNum + timestamp)).toString(32)
}

判断元素受否有该class

/**
 * Check if an element has a class
 * @param {HTMLElement} elm
 * @param {string} cls
 * @returns {boolean}
 */
export function hasClass(ele, cls) {
  return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}

元素添加class

/**
 * Add class to element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function addClass(ele, cls) {
  if (!hasClass(ele, cls)) ele.className += ' ' + cls
}

获取hasClass

/**
 * Remove class from element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function removeClass(ele, cls) {
  if (hasClass(ele, cls)) {
    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
    ele.className = ele.className.replace(reg, ' ')
  }
}

时间格式化,不包含时分秒

/**
 * 时间格式化,不包含时分秒
 * @param _date
 * @returns {string}
 * @constructor
 */
export function FormatDateNoTime(_date) {
  if (_date === '' || _date === null || _date === undefined) {
    return ''
  } else {
    const now = new Date(_date)
    const year = now.getFullYear()
    const month = now.getMonth() + 1 < 10 ? '0' + (now.getMonth() + 1) : now.getMonth() + 1
    const date = now.getDate() < 10 ? '0' + now.getDate() : now.getDate()
    return year + '/' + month + '/' + date
  }
}

生成年份

<el-form-item label="统计年度" prop="opspTrtCode">
  <el-select v-model="searchForm.opspTrtCode" clearable placeholder="请选择统计年度" allow-create style="width:100%"
    :fetch-suggestions="searchType" :trigger-on-focus="false">
    <el-option label="全部" value="" />
    <el-option v-for="item in initSelectYear()" :key="item.value" :label="item.label" :value="item.value" />
  </el-select>
</el-form-item>
initSelectYear() {
  var myDate = new Date()
  var year = myDate.getFullYear() // 获取当前年
  var years = []
  for (let i = 0; i < 30; i++) {
    years.push({ value: year - i, label: year - i + '年' })
  }
  return years
}

地图组件标点

<template>
  <div class="interzone-map">
    <div id="interzoneMap" />
  </div>
</template>
<script>
import echarts from 'echarts'
import './map/js/china'
// import './map/js/province/anhui'
// import './map/js/province/zhejiang'
// import './map/js/province/aomen'
// import './map/js/province/xianggang'
// import './map/js/province/guangdong'
// import './map/js/province/jiangxi'
// import './map/js/province/jiangsu'
// import './map/js/province/fujian'
// import './map/js/province/gansu'
// import './map/js/province/guizhou'
// import './map/js/province/sichuan'
// import './map/js/province/hubei'
export default {
  name: 'EchartMap',
  components: {},
  mixins: [],
  props: {},
  data() {
    return {
      myChart: null,
      map_name: 'china', // 地图
      mapData: [],
      geoCoordMap: {}
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.myChart = echarts.init(document.getElementById('interzoneMap'))
    window.onresize = () => {
      // 根据窗口大小调整曲线大小
      this.myChart.resize()
    }
    this.init_map() // 地图初始化
  },
  methods: {
    init_map() {
      var data = [
        { name: '海门', value: 88.76 },
        { name: '鄂尔多斯', value: 88.6 },
        { name: '招远', value: 39.21 },
        { name: '舟山', value: 87.54 },
        { name: '齐齐哈尔', value: 19.64 },
        { name: '盐城', value: 54.2 }
      ]
      var geoCoordMap = {
        海门: [121.15, 31.89],
        鄂尔多斯: [109.781327, 39.608266],
        招远: [120.38, 37.35],
        舟山: [122.207216, 29.985295],
        齐齐哈尔: [123.97, 47.33],
        盐城: [120.13, 33.38]
      }
      // 获取数据和坐标
      var convertData = function(data) {
        var res = []
        for (var i = 0; i < data.length; i++) {
          var geoCoord = geoCoordMap[data[i].name]
          if (geoCoord) {
            res.push({
              name: data[i].name,
              value: geoCoord.concat(data[i].value)
            })
          }
        }
        return res
      }

      var optionMap = {
        backgroundColor: '#FFFFFF',
        title: {
          text: '全国参保覆盖情况',
          subtext: '2020年',
          x: 'center'
        },
        // 提示框
        tooltip: {
          trigger: 'item',
          formatter: function(params) {
            // 添加数字,否则为坐标
            return (
              params.name +
              '<br>' +
              '参保覆盖率' +
              '' +
              ':' +
              '' +
              params.value[2] +
              '' +
              '%'
            )
          },
          padding: [
            5, // 上
            10, // 右
            5, // 下
            10 // 左
          ],
          textStyle: {
            color: '#fff',
            fontSize: '13'
          }
        },
        // 左侧小导航图标
        visualMap: {
          show: false
        },
        // 地图
        geo: {
          map: 'china',
          roam: false,
          itemStyle: {
            // 正常状态下
            normal: {
              areaColor: '#ffe7b2',
              borderColor: '#111'
            },
            // 选定某一区域下 相当于 hover
            emphasis: {
              areaColor: '#ff6341'
            }
          },
          z: 1
        },
        // 配置属性
        series: [
          {
            name: '参保覆盖率',
            type: 'scatter',
            coordinateSystem: 'geo',
            data: convertData(data),
            roam: false,
            label: {
              normal: {
                show: true // 省份名称
              },
              emphasis: {
                show: false
              }
            },
            symbolSize: function(val) {
              return val[2] / 8 // 也可以根据这里的数值更改大小  val[2] / x  x越小,标点越大
            }
          },
          {
            name: '参保覆盖率',
            type: 'effectScatter',
            coordinateSystem: 'geo',
            data: convertData(
              data
                .sort(function(a, b) {
                  // 这里是多个数据比较大小
                  return b.value - a.value
                })
                .slice(0, 1000)
            ), // slice里面的数可以是0 ,意思是全部显示  0,1000 意思是显示这组数据中最大前1000组
            symbolSize: function(val) {
              return val[2] / 5
            },
            showEffectOn: 'render',
            rippleEffect: {
              brushType: 'stroke'
            },
            hoverAnimation: true,
            label: {
              normal: {
                formatter: '{b}',
                position: 'right',
                show: false
              },
              emphasis: {
                show: false
              }
            }
          }
        ]
      }
      // 使用制定的配置项和数据显示图表
      this.myChart.setOption(optionMap)
      this.myChart.on('click', (params) => {
        console.log(params)
      })
    }
  }
}
</script>
<style scoped lang="scss">
.interzone-map {
  width: 100%;
  height: 100%;
  #interzoneMap {
    width: 100%;
    height: 100%;
  }
}
</style>

Echart组件封装

<template>
  <div class="My_Charts_Wrapper">
    <div :id="id" :class="className" class="My_Charts" :style="{height:height,width:width}" />
  </div>
</template>

<script>
import echarts from 'echarts'
require('echarts/theme/macarons')
import resize from './mixins/resize'

export default {
  name: 'MyCharts',
  components: {},
  mixins: [resize],
  props: {
    id: {
      type: String,
      default: 'myCharts'
    },
    className: {
      type: String,
      default: 'chart'
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '100%'
    },
    options: {
      type: Object,
      default: function() {
        return {}
      }
    }
  },
  data() {
    return {
      chart: null,
      defaultOptions: {
        grid: { left: '3%', right: '4%', bottom: '3%', top: '30', containLabel: true },
        tooltip: {
          trigger: 'axis',
          axisPointer: { type: 'shadow' }
        }
      }
    }
  },
  computed: {},
  watch: {
    options: {
      deep: true,
      handler(val) {
        this.setOptions(Object.assign(val, this.options))
      }
    }
  },
  created() {
  },
  mounted() {
    this.$nextTick(() => {
      this.init()
    })
  },
  beforeDestroy() {
    if (!this.chart) {
      return
    }
    this.chart.dispose()
    this.chart = null
  },
  methods: {
    init() {
      this.chart = echarts.init(document.getElementById(this.id), 'macarons')
      this.setOptions()
    },
    setOptions() {
      this.chart.setOption(Object.assign(this.defaultOptions, this.options))
    }
  }
}
</script>

<style scoped lang="scss">
  .My_Charts_Wrapper {
    width: 100%;
    height: 100%;
  }
  .My_Charts {
    >div {
      margin: 0 auto;
    }
    /deep/ .charts-table {
      width: 100%;
      border: 1px solid #dfe6ec;
      text-align: center;
      border-collapse: collapse;
      >thead{
        >tr{
          >th{
            height: 40px;
            background: #F0F2F5;
            border-right: 1px solid #cecece;
            border-bottom: 1px solid #dfe6ec;
            &:last-child {
              border-right: none;
            }
          }
        }
      }
      >tbody {
        >tr{
          >td{
            height: 40px;
            border-right: 1px solid #cecece;
            border-bottom: 1px solid #dfe6ec;
            &:last-child {
              border-right: none;
            }
          }
          &:last-child {
            >td {
              border-bottom: none;
            }
          }
        }
      }
    }
  }
</style>

引入码表字段

import { getInsutype } from '@/api/PaymentInAdvance/PaymentInAdvanceManage/Request'
import { CHK_STAS } from '@/utils/constant'

获取码表对应字段,塞入下拉

import { getCodeTableDetailConvergence } from '@/api/Common/Request'
// 查码表
getCodeTableDetailConvergence() {
  const codeType = CHK_STAS
  getCodeTableDetailConvergence({ codeType }).then(res => {
    this.$set(this.itemsDatas[2], 'options', res.data[CHK_STAS])
  })
}

引入colunms

import Columns from './columns'

标准参考页面

<!-- 单位待转金查询 -->
<template>
  <normal-layer
    :search-number="itemsDatas.length"
    title-name="单位待转金查询列表"
    title-need-handle
  >
    <template slot="search-header">
      <form-items :items-datas="itemsDatas" :form-datas="queryForm">
        <el-button @click="reset">重置</el-button>
        <el-button type="primary" @click="search">查询</el-button>
      </form-items>
    </template>
    <template slot="title-btns">
      <el-button type="primary" @click="printClick">打印</el-button>
    </template>
    <my-table-view
      v-loading="loading"
      :data="tableData"
      :columns="columns"
    />
    <Pagination />
  </normal-layer>
</template>
<script>
import NormalLayer from '@/views/components/PageLayers/normalLayer'
import FormItems from '@/views/components/PageLayers/form-items'
import PageHandle from '@/mixins/pageHandle'
import Columns from './listCloumns'
export default {
  components: {
    NormalLayer,
    FormItems
  },
  mixins: [PageHandle],
  data() {
    return {
      loading: false,
      itemsDatas: [
        { label: '单位编号', prop: 'xxx', type: 'input' },
        { label: '单位名称', prop: 'xxx', type: 'input' },
        { label: '单位类型', prop: 'xxx', type: 'select' }
      ],
      queryForm: {
        xxx: ''
      },
      columns: Columns,
      tableData: [
        { name: '白兰花', code: 'xxx', nameCode: 'xxx' },
        { name: '白兰花', code: 'xxx', nameCode: 'xxx' }
      ]
    }
  },
  watch: {
  },
  methods: {
    printClick() {
      window.print()
    }
  }
}
</script>
<style lang='scss' scoped>
</style>

审核和批量审核弹窗

<audit-dialog v-model="showAuditDialog" dialog-title="单位注销审核" @submit="submit" @closeAll="AuditDialogIsShow" />
import AuditDialog from '@/views/components/AuditDialog'
// 批量审核
    batchAuditClick() {
      if (this.multipleSelection.length <= 0) {
        this.$msgConfirm('请选择')
      } else {
        this.showAuditDialog = true
      }
    },

表格详情弹窗

 <!-- 详情弹窗 -->
    <Details :show="DetaVisible" @update:show="DetaIsShow" />
DetaIsShow() {
 this.DetaVisible = false
}
<my-table-view
      v-loading="loading"
      :data="tableData"
      :columns="columns"
      :multiple-selection.sync="multipleSelection"
    >
      <template slot="operation" slot-scope="scope">
        <my-button icon="detail" @click="detailsClick(scope.row)" />
        <my-button icon="audit" @click="review(scope.row)" />
      </template>
    </my-table-view>
detailsClick(row) {
      this.DetaVisible = true
    }

获取查询条件

search() {
    let params = {}
    params = Object.assign({}, this.searchForm)
    console.log('查询条件', params)
}

常用字段

'formCode':'表格编码'
'formNum':'金额'
'formDepart':'制表部门'
'formTime':'制表时间'
'formDealer':'经办人'
'status':'状态'
'operation':'操作'

获取银行类型

import { BANK_TYPE_CODE } from '@/utils/constant'
import { getCodeTableDetailConvergence } from '@/api/Common/Request'

getTableCode() {
    getCodeTableDetailConvergence({ codeType: BANK_TYPE_CODE }).then(res => {
        this.$set(this.itemsDatas[2], 'options', res.data[BANK_TYPE_CODE])
    })
}

搜索重置

<template slot="search-header">
  <form-items ref="queryForm" :items-datas="itemsDatas" :form-datas="queryForm" :rules="rules" :model="queryForm">
    <el-button @click="reset('queryForm')">重置</el-button>
    <el-button type="primary" @click="search">查询</el-button>
  </form-items>
</template>
reset(formName) {
    this.$refs[formName]['elForm'].resetFields()
}

批量审核

<template slot="title-btns">
  <el-button type="primary" @click="addClick">批量审核</el-button>
</template>
addClick(row) {
    if (this.multipleSelection.length > 0) {
        this.showAuditDialog = true
    } else {
        this.$msgError('请至少选择一行!')
    }
}
submit(v) {
    const params = {
        rchkFlag: v.data.statue,
        memo: v.data.content,
        taxItrcEvtId: this.multipleSelection.map(item => item.taxItrcEvtId)
    }
    // 保存灵活就业征集信息发送审核
    Api.saveTaxFlexiblePaySendAud(params).then(res => {
        if (res.code === 0) {
            v.fn()
            this.search()
        } else {
            this.$msgError(res.message)
        }
    })
}

vue项目开发笔记记录(三):https://developer.aliyun.com/article/1483520

相关文章
|
21天前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
161 0
|
22天前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
1月前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
137 17
|
1月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
74 1
|
1月前
|
存储 JavaScript 前端开发
如何高效实现 vue 文件批量下载及相关操作技巧
在Vue项目中,实现文件批量下载是常见需求。例如文档管理系统或图片库应用中,用户可能需要一次性下载多个文件。本文介绍了三种技术方案:1) 使用`file-saver`和`jszip`插件在前端打包文件为ZIP并下载;2) 借助后端接口完成文件压缩与传输;3) 使用`StreamSaver`解决大文件下载问题。同时,通过在线教育平台的实例详细说明了前后端的具体实现步骤,帮助开发者根据项目需求选择合适方案。
93 0
|
3月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
350 4
|
2月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
229 77
|
1月前
|
监控 JavaScript 前端开发
Vue 文件批量下载组件封装完整使用方法及优化方案解析
本文详细介绍了批量下载功能的技术实现与组件封装方案。主要包括两种实现方式:**前端打包方案(基于file-saver和jszip)** 和 **后端打包方案**。前者通过前端直接将文件打包为ZIP下载,适合小文件场景;后者由后端生成ZIP文件流返回,适用于大文件或大量文件下载。同时,提供了可复用的Vue组件`BatchDownload`,支持进度条、失败提示等功能。此外,还扩展了下载进度监控和断点续传等高级功能,并针对跨域、性能优化及用户体验改进提出了建议。可根据实际需求选择合适方案并快速集成到项目中。
158 17
|
3月前
|
缓存 JavaScript 前端开发
Vue 基础语法介绍
Vue 基础语法介绍
|
1月前
|
JavaScript 前端开发 UED
Vue 手风琴实现的三种常用方式及长尾关键词解析
手风琴效果是Vue开发中常见的交互组件,可节省页面空间、提升用户体验。本文介绍三种实现方式:1) 原生Vue结合数据绑定与CSS动画;2) 使用Element UI等组件库快速构建;3) 自定义指令操作DOM实现独特效果。每种方式适用于不同场景,可根据项目需求选择。示例包括产品特性页、后台菜单及FAQ展示,灵活满足多样需求。附代码示例与资源链接,助你高效实现手风琴功能。
102 10