vue2第三章

发布时间 2023-08-17 15:51:47作者: 丶乔

1.内置组件component

渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。

vue内置组件

<template>
    <component
        :is="viewName"
        @updateView="updateView"
    >
    </component>
</template>
<script>
 
          import studentGrowthPortfolio from './studentGrowthPortfolio.vue';  //学生 index
import fileDetails from './fileDetails.vue';  //成长档案 详情
export default {
  data(){
    return{
      viewName:"studentGrowthPortfolio",
    }
  },
  components:{
    studentGrowthPortfolio,
    fileDetails
  },
  mounted(){
    this.viewName=this.$route.query.viewName?this.$route.query.viewName:this.viewName;
  },
  created () {
  },
  methods:{
    /**
         * 接收子组件数据
         * @param data {Object}
         * @return {Void} 无
         */
    updateView(name){
      this.viewName = name
      if(!name){
        this.$routePlugin.delQuery('viewName');
      }else{
        this.$routePlugin.addQuery({viewName:name});
      }
    },
  },
}
</script>
<style scoped lang="less">
  @import "~@/libs/less/theme/theme.less";

</style>

2.vue中使用svg-icon

1 在src目录新建一个icons目录,icons下添加svg文件夹,svg下面放iconfont的icon标签的svg标签文件,安装依赖

npm install svg-sprite-loader --save-dev

2.icons目录下建index.js

import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件
 
// register globally
Vue.component('svg-icon', SvgIcon)
 
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
const iconMap = requireAll(req)

3.封装一下icon,在components下新建SvgIcon文件

<template>
  <svg :class="svgClass" aria-hidden="true">
    <use :xlink:href="iconName"/>
  </svg>
</template>
 
<script>
  export default {
    name: 'SvgIcon',
    props: {
      iconClass: {
        type: String,
        required: true
      },
      className: {
        type: String,
        default: ''
      }
    },
    computed: {
      iconName() {
        return `#icon-${this.iconClass}`
      },
      svgClass() {
        if (this.className) {
          return 'svg-icon ' + this.className
        } else {
          return 'svg-icon'
        }
      }
    }
  }
</script>
 
<style scoped>
  .svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
  }
</style>

4.在main.js中引入svg组件

5.vue.config.js的配置

'use strice'

const path = require('path')
const isProd = process.env.NODE_ENV === 'production'
const defaultSettings = require('./src/settings.js')
const name = defaultSettings.title || 'vue Element Admin' // page title

function resolve(dir) {
  return path.join(__dirname, dir)
}

// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {
  /**
   * You will need to set publicPath if you plan to deploy your site under a sub path,
   * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
   * then publicPath should be set to "/bar/".
   * In most cases please use '/' !!!
   * Detail: https://cli.vuejs.org/config/#publicpath
   */
  publicPath: isProd ? './' : '/',
  lintOnSave: !isProd,
  // productionSourceMap: false,
  devServer: {
    open: true,
    overlay: {
      warnings: false,
      errors: true,
    },
    // before: require('./mock/mock-server.js'),
    proxy: {
      [process.env.VUE_APP_BASE_API]: {
        target: process.env.VUE_APP_REMOTE_URL,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: '',
        },
      },
    },
  },
  configureWebpack: {
    // provide the app's title in webpack's name field, so that
    // it can be accessed in index.html to inject the correct title.
    name: name,
  },
  chainWebpack(config) {
    // set svg-sprite-loader
    config.module.rule('svg').exclude.add(resolve('src/icons')).end()

    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]',
      })
      .end()

    // set preserveWhitespace
    config.module
      .rule('vue')
      .use('vue-loader')
      .loader('vue-loader')
      .tap(options => {
        options.compilerOptions.preserveWhitespace = true
        return options
      })
      .end()

    config.when(isProd, config => {
      config
        .plugin('preload')
        .tap(options => {
          options[0].fileBlacklist = options[0].fileBlacklist || []
          options[0].fileBlacklist.push(/runtime\..*\.js$/)
          return options
        })
        .end()

      config
        .plugin('ScriptExtHtmlWebpackPlugin')
        .after('html')
        .use('script-ext-html-webpack-plugin', [
          {
            // `runtime` must same as runtimeChunk name. default is `runtime`
            inline: /runtime\..*\.js$/,
          },
        ])
        .end()

      config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          libs: {
            name: 'chunk-libs',
            test: /[\\/]node_modules[\\/]/,
            priority: 10,
            chunks: 'initial', // only package third parties that are initially dependent
          },
          elementUI: {
            name: 'chunk-elementUI', // split elementUI into a single package
            priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
            test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
          },
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'), // can customize your rules
            minChunks: 3, //  minimum common number
            priority: 5,
            reuseExistingChunk: true,
          },
        },
      })

      config.optimization.runtimeChunk('single')

      config
        .plugin('HtmlHook')
        .use(HtmlHook, [
          {
            version: require('./package.json').version,
            date: new Date().toLocaleDateString(),
          },
        ])
        .end()
    })
  },
}

function HtmlHook(options) {
  this.options = options || {}
}

HtmlHook.prototype.apply = function (compiler) {
  compiler.hooks.compilation.tap('HtmlHook', compilation => {
    const onBeforeEmit = (htmlPluginData, callback) => {
      htmlPluginData.html +=
        '\n<!-- ' +
        Object.entries(this.options)
          .map(([key, value]) => `${key}: ${value}`)
          .join(' | ') +
        ' -->'
      callback(null, htmlPluginData)
    }

    // HtmlWebPackPlugin - new
    if (compilation.hooks.htmlWebpackPluginAfterHtmlProcessing) {
      compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tapAsync(
        'HtmlHook',
        onBeforeEmit,
      )
    } else {
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      if (HtmlWebpackPlugin.getHooks) {
        const hooks = HtmlWebpackPlugin.getHooks(compilation)
        const htmlPlugins = compilation.options.plugins.filter(
          plugin => plugin instanceof HtmlWebpackPlugin,
        )
        if (htmlPlugins.length === 0) {
          const message =
            "Error running html-hook, are you sure you have html-webpack-plugin before it in your webpack config's plugins?"
          throw new Error(message)
        }
        hooks.beforeEmit.tapAsync('HtmlHook', onBeforeEmit)
      } else {
        const message =
          "Error running html-hook, are you sure you have html-webpack-plugin before it in your webpack config's plugins?"
        throw new Error(message)
      }
    }
  })
}

3.暂无数据通用组件

<script>
export default {
  functional: true,
  render(h, { props, children, data }) {
    return (
      <div class="c__empty" {...data}>
        {children || [
          <svg-icon icon-class="empty" />,
          <p>{props.tips || '暂无数据'}</p>,
        ]}
      </div>
    )
  },
}
</script>

<style lang="scss">
.c__empty {
  text-align: center;
  color: #8c8c8c;

  .svg-icon {
    font-size: 60px;
  }
}
</style>

4.行内style backgroundImage

<div :style="{ backgroundImage: 'url(' + bgUrl + ')' }" style="height:500px">
  
  </div>

5.vue中的 Vue.sync修饰符与this.$emit(‘update:xxx‘, newXXX)

以及配合使用computed的get和set方法

一 Vue中的数据是单向数据流:父级 prop 的更新会向下流动到子组件中,但是反过来则不行,即子组件无法直接修改父组件中的数据。
但我们常常会遇到需要在子组件中修改父组件数据的场景。.sync修饰符就可以帮助我们实现这种功能。

Vue通过prop进行双向数据绑定。子组件数据变化,一般只能通过 this.$emit(func, val) 回调父组件函数来传值给父组件。

Vue2.3版本引入sync,作为一个事件绑定语法糖,当子组件触发事件时,父组件会响应事件并实现数据更新。

computed中有get和set方法,(在默认的情况下只有get)

1.get方法是取,相当于我们可以在get中给这个计算属性中的变量赋值
2.set方法是改变时触发,这里的改变指的是当我们在computed中定义的变量的值发生改变是,会触发set方法,这样我们就可以在set方法中进行一些我们想要做的事,(比如说调用某个方法)

父组件
 <Execute :isOpenExecute.sync="isOpenExecute" :rowId="rowId" />



  
子组件 (可以直接使用computed来配合改变父组件的属性值)
<template>
  <el-dialog title="活动执行" :visible.sync="executeVisible" width="30%">
    <span>内容</span>
    <span slot="footer" class="dialog-footer">
      <el-button @click="$emit('update:isOpenExecute', false)">取 消</el-button>
      <el-button type="primary" @click="$emit('update:isOpenExecute', false)"
        >确 定</el-button
      >
    </span>
  </el-dialog>
</template>

<script>
export default {
  data() {
    return {}
  },
  props: {
    isOpenExecute: {
      type: Boolean,
      default: false,
    },
    rowId: String,
  },
  computed: {
    executeVisible: {
      get(e) {
        console.log(e.isOpenExecute, e.rowId)
        //打开弹窗获取详情
        if (e.isOpenExecute) {
          this.getInfo(e.rowId)
        }
        return this.isOpenExecute
      },
      set(e) {
        this.$emit('update:isOpenExecute', false)
      },
    },
  },
  methods: {
    getInfo(id) {
      console.log(id, '获取详情')
    },
  },
}
</script>

<style lang="scss" scoped></style>

6.自定义弹窗(带输入)

 this.$prompt('', '原因', {
        inputType: 'textarea',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputPattern: /\S/,
        inputErrorMessage: '原因不能为空',
        inputValidator: (value) => {
          // 点击按钮时,对文本框里面的值进行验证
          if(!value) {
            return inputErrorMessage;
          }
        }
      }).then(({ value }) => {
        console.log('输入框的值',value);
      }).catch(() => {

      })

7.导出表格数据

import table2excel from "@/utils/exccel";
   handleExport() {
    var column = [];
    (this.$refs["myTable"] as any).$children.forEach((element) => {
      // console.log(element);

      if (element.title && element.title != "操作") {
        let temp = {
          title: element.title,
          key: element.field, //key值对应表单数据字段名称
          type: "text",
        };
        column.push(temp);
      }
    });
    table2excel(column, this.tplDataLs, "模板"); //生成Excel表格,自动下载
  }
/* eslint-disable */
let idTmr;
const getExplorer = () => {
  let explorer = window.navigator.userAgent;
  //ie
  if (explorer.indexOf("MSIE") >= 0) {
    return "ie";
  }
  //firefox
  else if (explorer.indexOf("Firefox") >= 0) {
    return "Firefox";
  }
  //Chrome
  else if (explorer.indexOf("Chrome") >= 0) {
    return "Chrome";
  }
  //Opera
  else if (explorer.indexOf("Opera") >= 0) {
    return "Opera";
  }
  //Safari
  else if (explorer.indexOf("Safari") >= 0) {
    return "Safari";
  }
};
// 判断浏览器是否为IE
const exportToExcel = (data, name) => {
  // 判断是否为IE
  if (getExplorer() == "ie") {
    tableToIE(data, name);
  } else {
    tableToNotIE(data, name);
  }
};

const Cleanup = () => {
  window.clearInterval(idTmr);
};

// ie浏览器下执行
const tableToIE = (data, name) => {
  let curTbl = data;
  let oXL = new ActiveXObject("Excel.Application");

  //创建AX对象excel
  let oWB = oXL.Workbooks.Add();
  //获取workbook对象
  let xlsheet = oWB.Worksheets(1);
  //激活当前sheet
  let sel = document.body.createTextRange();
  sel.moveToElementText(curTbl);
  //把表格中的内容移到TextRange中
  sel.select;
  //全选TextRange中内容
  sel.execCommand("Copy");
  //复制TextRange中内容
  xlsheet.Paste();
  //粘贴到活动的EXCEL中

  oXL.Visible = true;
  //设置excel可见属性

  try {
    let fname = oXL.Application.GetSaveAsFilename(
      "Excel.xls",
      "Excel Spreadsheets (*.xls), *.xls"
    );
  } catch (e) {
    print("Nested catch caught " + e);
  } finally {
    oWB.SaveAs(fname);

    oWB.Close((savechanges = false));
    //xls.visible = false;
    oXL.Quit();
    oXL = null;
    // 结束excel进程,退出完成
    window.setInterval("Cleanup();", 1);
    idTmr = window.setInterval("Cleanup();", 1);
  }
};

// 非ie浏览器下执行
const tableToNotIE = (function() {
  // 编码要用utf-8不然默认gbk会出现中文乱码
  const uri = "data:application/vnd.ms-excel;base64,",
    template =
      '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>';

  const base64 = function(s) {
    return window.btoa(unescape(encodeURIComponent(s)));
  };

  const format = (s, c) => {
    return s.replace(/{(\w+)}/g, (m, p) => {
      return c[p];
    });
  };

  return (table, name) => {
    const ctx = {
      worksheet: name,
      table
    };

    const url = uri + base64(format(template, ctx));

    if (navigator.userAgent.indexOf("Firefox") > -1) {
      window.location.href = url;
    } else {
      const aLink = document.createElement("a");
      aLink.href = url;
      aLink.download = name || "";
      let event;
      if (window.MouseEvent) {
        event = new MouseEvent("click");
      } else {
        event = document.createEvent("MouseEvents");
        event.initMouseEvent(
          "click",
          true,
          false,
          window,
          0,
          0,
          0,
          0,
          0,
          false,
          false,
          false,
          false,
          0,
          null
        );
      }
      aLink.dispatchEvent(event);
    }
  };
})();

const resolveOptions = options => {
  if (options.length === 1) {
    return options[0];
  }

  return {
    column: options[0] || [],
    data: options[1] || [],
    excelName: options[2] || "",
    captionName: options[3]
  };
};

// 导出函数
const table2excel = (...options) => {
  function getTextHtml(val) {
    return `<td style="text-align: center">${val}</td>`;
  }

  function getImageHtml(val, options) {
    options = Object.assign({ width: 40, height: 60 }, options);
    return `<td style="width: ${options.width}px; height: ${
      options.height
    }px; text-align: center; vertical-align: middle"><img src="${val}" width=${options.width *
      0.99} height=${options.height * 0.99} /></td>`;
  }

  const typeMap = {
    image: getImageHtml,
    text: getTextHtml
  };

  const { column, data, excelName, captionName } = resolveOptions(options);

  let caption = captionName
    ? `<caption style="font-weight:bold">${captionName}</caption>`
    : "";

  let thead = column.reduce((result, item) => {
    result += `<th>${item.title}</th>`;
    return result;
  }, "");

  thead = `<thead><tr>${thead}</tr></thead>`;

  let tbody = data.reduce((result, row) => {
    const temp = column.reduce((tds, col) => {
      const options = {};

      if (col.type === "image") {
        if (row.size) {
          options.width = row.size[0];
          options.height = row.size[1];
        } else {
          col.width && (options.width = col.width);
          col.height && (options.height = col.height);
        }
      }

      tds += typeMap[col.type || "text"](row[col.key], options);

      return tds;
    }, "");

    result += `<tr>${temp}</tr>`;

    return result;
  }, "");

  tbody = `<tbody>${tbody}</tbody>`;

  const table = caption + thead + tbody;

  // 导出表格
  exportToExcel(table, excelName);
};

export default table2excel;