二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表

1. 进阶语法

1.1 v-model 简化代码

App.vue
<template>
  <!-- 11-src-下拉封装 -->
  <div class="app">
    <!-- <BaseSelect :cityId="selectId" @changeId="handleChangeId"></BaseSelect> -->

    <!-- v-model 简化代码 → 父组件 v-model 简化代码,实现 子组件 和 父组件数据 双向绑定 -->
    <!-- 1. 子组件中:props 通过 value 接收,事件触发 input -->
    <!-- 2. 父组件中:v-model 给组件直接绑数据 -->
    <BaseSelect :cityId="selectId" v-model="selectId"></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from "./components/BaseSelect.vue";
export default {
  data() {
    return {
      selectId: "102",
    };
  },
  components: {
    BaseSelect,
  },
  methods: {
    /* handleChangeId(id) {
      this.selectId = id;
    }, */
  },
  /* watch: {
    selectId(newval) {
      console.log(newval);
    },
  }, */
};
</script>

<style>
</style>

BaseSelect.vue

<template>
  <div>
    <!-- 
      表单类组件封装 & v-model 简化代码
      实现子组件和父组件数据的双向绑定 (实现App.vue中的selectId和子组件选中的数据进行双向绑定)
      
      下拉菜单 → value 和 input 事件的语法糖
      model 不能用 → 双向绑定,代表要修改数据 → cityId来自于父组件,子组件不能直接修改
     -->
    <select :value="cityId" @change="changeId">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    cityId: String,
  },
  methods: {
    changeId(e) {
      // 通知父组件修改数据 → 当前下拉菜单的value值
      // this.$emit("changeId", e.target.value);

      this.$emit("input", e.target.value);
    },
  },
};
</script>

<style>
</style>

1.2 sync 修饰符

App.vue
<template>
  <!-- 12-src-sync修饰符 -->
  <div class="app">
    <button @click="isShow = true">退出按钮</button>
    <!-- isShow.sync  => :isShow="isShow" @update:isShow="isShow=$event" -->
    <!-- <BaseDialog v-show="isShow"></BaseDialog> -->

    <!-- <BaseDialog :isShow="isShow" @closeDialog="handleClose"></BaseDialog> -->

    <!--
      .sync 修饰符 → 可以实现 子组件 与 父组件数据 的 双向绑定,简化代码
      应用场景:封装弹框类的基础组件, visible属性 true显示 false隐藏
      .sync修饰符 就是 :属性名 和 @update:属性名 合写
    -->
    <BaseDialog :isShow.sync="isShow"></BaseDialog>
  </div>
</template>

<script>
import BaseDialog from "./components/BaseDialog.vue";
export default {
  data() {
    return {
      isShow: false,
    };
  },
  methods: {
    /* handleClose(newVal) {
      this.isShow = newVal;
    }, */
  },
  components: {
    BaseDialog,
  },
};
</script>

<style>
</style>

BaseDialog.vue

<template>
  <div class="base-dialog-wrap" v-show="isShow">
    <div class="base-dialog">
      <div class="title">
        <h3>温馨提示:</h3>
        <button class="close" @click="close">x</button>
      </div>
      <div class="content">
        <p>你确认要退出本系统么?</p>
      </div>
      <div class="footer">
        <button>确认</button>
        <button>取消</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    isShow: Boolean,
  },
  methods: {
    close() {
      // this.$emit("closeDialog", false);

      this.$emit("update:isShow", false);
    },
  },
};
</script>

<style scoped>
.base-dialog-wrap {
  width: 300px;
  height: 200px;
  box-shadow: 2px 2px 2px 2px #ccc;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 0 10px;
}
.base-dialog .title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid #000;
}
.base-dialog .content {
  margin-top: 38px;
}
.base-dialog .title .close {
  width: 20px;
  height: 20px;
  cursor: pointer;
  line-height: 10px;
}
.footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 26px;
}
.footer button {
  width: 80px;
  height: 40px;
}
.footer button:nth-child(1) {
  margin-right: 10px;
  cursor: pointer;
}
</style>

1.3 ref 和 $refs

1.3.1 获取 dom

App.vue
<template>
  <!-- 13-src-ref-DOM -->
  <div class="app">
    <div class="base-chart-box">父组件</div>
    <BaseChart></BaseChart>
  </div>
</template>

<script>
import BaseChart from "./components/BaseChart.vue";
export default {
  components: {
    BaseChart,
  },
};
</script>

<style>
.base-chart-box {
  width: 300px;
  height: 200px;
}
</style>
BaseChart.vue
<template>
  <!-- 
    ref 和 $refs 
    作用:利用 ref 和 $refs 可以用于 获取 dom 元素, 或 组件实例
    特点:查找范围 → 当前组件内 (更精确稳定)
  -->
  <!-- 1. 目标标签 – 添加 ref 属性 -->
  <div class="base-chart-box" ref="chartEl">子组件</div>
</template>

<script>
import * as echarts from "echarts";

export default {
  mounted() {
    // 基于准备好的dom,初始化echarts实例
    // document.querySelector 会查找项目中所有的元素
    // $refs只会在当前组件查找盒子
    // var myChart = echarts.init(document.querySelector('.base-chart-box'))

    // 2. 恰当时机, 通过 this.$refs.xxx, 获取目标标签
    var myChart = echarts.init(this.$refs.chartEl);
    // 绘制图表
    myChart.setOption({
      title: {
        text: "ECharts 入门示例",
      },
      tooltip: {},
      xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
      },
      yAxis: {},
      series: [
        {
          name: "销量",
          type: "bar",
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    });
  },
};
</script>

<style scoped>
.base-chart-box {
  width: 400px;
  height: 300px;
  border: 3px solid #000;
  border-radius: 6px;
}
</style>

1.3.2 获取组件

App.vue
<template>
  <!-- 14-src-ref-组件 -->
  <div class="app">
    <!-- 通过这种方式,可以在父组件中直接操作子组件的方法和数据,这在需要跨组件通信时非常有用。 -->
    <!-- 1. 目标组件 – 添加 ref 属性 -->
    <BaseForm ref="myForm"></BaseForm>
    <button @click="getValues">获取数据</button>
    <button @click="resetValues">重置数据</button>
  </div>
</template>

<script>
import BaseForm from "./components/BaseForm.vue";
export default {
  components: {
    BaseForm,
  },
  methods: {
    getValues() {
      // 2. 恰当时机, 通过 this.$refs.xxx, 获取目标组件,就可以调用组件对象里面的方法
      // this.$refs.myForm → BaseForm 组件的 Vue 实例对象
      console.log(this.$refs.myForm); // BaseForm vue对象
      console.log(this.$refs.myForm.getValues()); // {username: '12', password: '12'}
    },
    resetValues() {
      this.$refs.myForm.resetValues();
    },
  },
};
</script>

<style>
.app {
  width: 300px;
  height: 200px;
  border: 1px solid #000;
  border-radius: 10px;
  padding-top: 30px;
  padding-left: 30px;
}
button {
  margin-top: 50px;
  margin-left: 30px;
}
</style>
BaseForm.vue
<template>
  <form action="">
    账号:<input type="text" v-model="username" /><br /><br />
    密码:<input type="text" v-model="password" />
  </form>
</template>

<script>
export default {
  data() {
    return {
      username: "",
      password: "",
    };
  },
  methods: {
    getValues() {
      return {
        username: this.username,
        password: this.password,
      };
    },
    resetValues() {
      this.username = "";
      this.password = "";
    },
  },
};
</script>

<style>
</style>

1.4 Vue异步更新、$nextTick

<template>
  <!-- 15-src-$nextTick -->
  <div class="app">
    <div v-if="isShowEdit">
      <input type="text" v-model="editValue" ref="ipt" />
      <button @click="hide">确认</button>
    </div>

    <div v-else>
      <span>{{ title }}</span>
      <button @click="show">编辑</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: "大标题",
      editValue: "",
      isShowEdit: false,
    };
  },
  methods: {
    // 需求:编辑标题, 编辑框自动聚焦
    show() {
      // 点击编辑,显示编辑框
      this.isShowEdit = true;

      // 让编辑框,立刻获取焦点
      // 1. 报错 Cannot read properties of undefined (reading 'focus')
      // ! Vue 是 异步更新 DOM (提升性能)
      // this.$refs.ipt.focus();

      // 2. 延迟慢 体验差
      /* setTimeout(()=>{
        this.$refs.ipt.focus();
      },1500) */

      // 3. Vue异步更新、$nextTick
      //    → 等 DOM 更新后, 才会触发执行此方法里的函数体
      // 语法: this.$nextTick(函数体)
      this.$nextTick(() => {
        this.$refs.ipt.focus();
      });
    },
    hide() {
      this.isShowEdit = false;
    },
  },
};
</script>

<style>
span {
  font-size: 30px;
  margin-right: 30px;
}
</style>

2. 自定义指令

2.1 全局&局部注册

main.js
import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

/* 
  指令介绍
  内置指令:v-html、v-if、v-bind、v-on... 这都是Vue给咱们内置的一些指令,可以直接使用
  自定义指令:同时Vue也支持让开发者,自己注册一些指令。这些指令被称为自定义指令
  每个指令都有自己各自独立的功能
*/

// 自定义指令
// 自定义指令:自己定义的指令,可以封装一些 dom 操作,扩展额外功能
// 1. 全局注册 - 语法
/* Vue.directive("指令名", {
  inserted(el) {
    // 对el标签扩展额外功能
  },
}); */
/* Vue.directive("focus", {
  // inserted 指令的生命周期钩子 → 当指令被加到标签上面自动执行的代码
  // el 使用指令的那个DOM元素
  // 自带一个形参 → 添加当前指令的标签
  inserted(el) {
    el.focus();
  },
}); */

new Vue({
  render: (h) => h(App),
}).$mount("#app");
App.vue
<template>
  <!-- 01-src-自定义指令-focus -->
  <div id="app">
    <h1>自定义指令</h1>
    <!-- 
      使用指令
      注意:在使用指令的时候,一定要先注册,再使用,否则会报错
      使用指令语法:v-指令名 如:<input type="text"  v-focus/>  
      注册指令时不用加v-前缀,但使用时一定要加v-前缀
     -->
    <input type="text" v-focus />
  </div>
</template>

<script>
export default {
  // 2. 局部注册 – 语法 指令名/'指令名'
  /* directives: {
    指令名: {
      inserted(el) {
        // 可以对 el 标签,扩展额外功能
        el.focus();
      },
    },
  }, */
  directives: {
    focus: {
      inserted(el) {
        el.focus();
      },
    },
  },
};
</script>

<style>
</style>

2.2 指令的值

<template>
  <!-- 02-src-自定义指令-值 -->
  <!-- 
    1. 通过指令的值相关语法,可以应对更复杂指令封装场景
    2. 指令值的语法:
    ① v-指令名 = "指令值",通过 等号 可以绑定指令的值
    ② 通过 binding.value 可以拿到指令的值
    ③ 通过 update 钩子,可以监听指令值的变化,进行dom更新操作
  -->
  <div id="app">
    <!-- 语法:在绑定指令时,可以通过"等号"的形式为指令 绑定 具体的参数值 -->
    <h1 v-color="color1">
      自定义指令
      <button @click="color1 = 'green'">修改颜色</button>
    </h1>
    <h1 v-color="color2">自定义指令</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      color1: "red",
      color2: "pink",
    };
  },
  // 需求:实现一个 color 指令 - 传入不同的颜色, 给标签设置文字颜色
  directives: {
    color: {
      // binding 对象会包含所有与该指令相关的信息
      inserted(el, binding) {
        // console.log(el);
        // console.log(binding);

        // 通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数
        // console.log(binding.value);

        el.style.color = binding.value;
      },
      update(el, binding) {
        el.style.color = binding.value;
      },
    },
  },
};
</script>

<style>
</style>

2.3 v-loading 指令封装

main.js
import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

// // 1. 全局注册指令
// Vue.directive('focus', {
//   // inserted 会在 指令所在的元素,被插入到页面中时触发
//   inserted (el) {
//     // el 就是指令所绑定的元素
//     // console.log(el);
//     el.focus()
//   }
// })

new Vue({
  render: (h) => h(App),
}).$mount("#app");
App.vue
<template>
  <!-- 03-src-封装loading指令 -->
  <!-- 
    场景:实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
    需求:封装一个 v-loading 指令,实现加载中的效果
  -->
  <div class="box" v-loading="isLoading">
    <ul>
      <li v-for="item in list" :key="item.id" class="news">
        <div class="left">
          <div class="title">{{ item.title }}</div>
          <div class="info">
            <span>{{ item.source }}</span>
            <span>{{ item.time }}</span>
          </div>
        </div>

        <div class="right">
          <img :src="item.img" alt="" />
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
// 安装axios =>  yarn add axios
import axios from "axios";

// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
  data() {
    return {
      list: [],
      isLoading: true,
    };
  },
  watch: {
    list: {
      deep: true,
      handler(newList) {
        // newList !== [] ? (this.isLoading = false) : (this.isLoading = true);
        this.isLoading = newList.length === 0;
      },
    },
  },
  async created() {
    // 1. 发送请求获取数据
    const res = await axios.get("http://hmajax.itheima.net/api/news");

    setTimeout(() => {
      // 2. 更新到 list 中,用于页面渲染 v-for
      this.list = res.data.data;
      // this.isLoading = false;
    }, 1000);
  },
  directives: {
    loading: {
      inserted(el, binding) {
        binding.value
          ? el.classList.add("loading")
          : el.classList.remove("loading");
      },
      update(el, binding) {
        binding.value
          ? el.classList.add("loading")
          : el.classList.remove("loading");
      },
    },
  },
};
</script>

<style>
/* 通过css添加一个伪元素 → 假的标签 */
/* ::before:这是一个伪元素选择器,用于在目标元素的内容之前插入额外的内容。
  这个伪元素并不存在于文档树中,但它会显示在目标元素的前面。
  常用于为正在加载的资源(如图片、视频或音频)添加自定义的加载样式,以提供更丰富的用户体验和视觉反馈。 */
/* 当将.loading和::before组合使用时,就表示在具有loading类的每个元素的内容之前插入额外的内容。 */
.loading::before {
  /* 必填属性 */
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url(https://blog.csdn.net/"./loading.gif") no-repeat center;
}

.box {
  width: 800px;
  min-height: 500px;
  border: 3px solid orange;
  border-radius: 5px;
  position: relative;
  margin: 0 auto;
}
.news {
  display: flex;
  height: 120px;
  width: 600px;
  margin: 0 auto;
  padding: 20px 0;
  cursor: pointer;
}
.news .left {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-right: 10px;
}
.news .left .title {
  font-size: 20px;
}
.news .left .info {
  color: #999999;
}
.news .left .info span {
  margin-right: 20px;
}
.news .right {
  width: 160px;
  height: 120px;
}
.news .right img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

3. 插槽

3.1 默认插槽

3.2 后备内容

3.3 具名插槽

App.vue
<template>
  <!-- 04-src-封装对话框组件-默认和具名插槽 -->
  <div>
    <!-- 一、默认插槽 -->
    <!-- <MyDialog>文本内容1</MyDialog> -->
    <!-- <MyDialog><h3>标题3</h3></MyDialog> -->

    <!-- 二、后备内容 -->
    <!-- <MyDialog></MyDialog> -->

    <!-- 三、具名插槽 -->
    <MyDialog>
      <template v-slot:head><h4>温馨提示</h4></template>
      <template #content>确定要删除吗?</template>
    </MyDialog>
  </div>
</template>

<script>
import MyDialog from "./components/MyDialog.vue";
export default {
  data() {
    return {};
  },
  components: {
    MyDialog,
  },
};
</script>

<style>
body {
  background-color: #b3b3b3;
}
</style>

MyDialog.vue

<template>
  <div class="dialog">
    <div class="dialog-header">
      <!-- <h3>友情提示</h3> -->
      <slot name="head"></slot>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <!-- 我是文本内容 -->
      <!-- 
      一、插槽 - 默认插槽 → 让组件内部的一些 结构 支持 自定义
        插槽基本语法:
        1. 组件内需要定制的结构部分,改用<slot></slot>占位
        2. 使用组件时, <MyDialog></MyDialog>标签内部, 传入结构替换slot
        3. 给插槽传入内容时,可以传入纯文本、html标签、组件
      -->
      <!-- <slot></slot> -->

      <!-- 
      二、插槽 - 后备内容(默认值)
        插槽后备内容:封装组件时,可以为预留的 `<slot>` 插槽提供后备内容(默认内容)。
        在 <slot> 标签内,放置内容, 作为默认显示内容 → 外部使用组件时,不传东西,则slot会显示后备内容
      -->
      <!-- <slot>默认内容</slot> -->

      <!-- 
      三、插槽 - 具名插槽 → 一个组件内有多处结构,需要外部传入标签,进行定制
        具名插槽语法:
        1. 多个slot使用name属性区分名字
        2. template配合v-slot:名字来分发对应标签
        3. v-slot:插槽名 可以简化成 #插槽名
      -->
      <slot name="content"></slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
};
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}
.dialog {
  width: 470px;
  height: 230px;
  padding: 0 25px;
  background-color: #ffffff;
  margin: 40px;
  border-radius: 5px;
}
.dialog-header {
  height: 70px;
  line-height: 70px;
  font-size: 20px;
  border-bottom: 1px solid #ccc;
  position: relative;
}
.dialog-header .close {
  position: absolute;
  right: 0px;
  top: 0px;
  cursor: pointer;
}
.dialog-content {
  height: 80px;
  font-size: 18px;
  padding: 15px 0;
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
}
.dialog-footer button {
  width: 65px;
  height: 35px;
  background-color: #ffffff;
  border: 1px solid #e1e3e9;
  cursor: pointer;
  outline: none;
  margin-left: 10px;
  border-radius: 3px;
}
.dialog-footer button:last-child {
  background-color: #007acc;
  color: #fff;
}
</style>

3.4 作用域插槽

App.vue
<template>
  <!--
    05-src-封装表格组件-作用域插槽
    场景:封装表格组件
    1. 父传子,动态渲染表格内容
    2. 利用默认插槽,定制操作列
    3. 删除或查看都需要用到 当前项的 id,属于组件内部的数据, 通过 作用域插槽 传值绑定,进而使用
  -->
  <div>
    <MyTable :data="list">
      <template #default="obj">
        <button @click="del(obj.id)">删除</button>
      </template>
    </MyTable>

    <MyTable :data="list2">
      <template #default="{ id, msg }">
        <button @click="fn(id, msg)">查看</button>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from "./components/MyTable.vue";
export default {
  data() {
    return {
      list: [
        { id: 1, name: "张小花", age: 18 },
        { id: 2, name: "孙大明", age: 19 },
        { id: 3, name: "刘德忠", age: 17 },
      ],
      list2: [
        { id: 1, name: "赵小云", age: 18 },
        { id: 2, name: "刘蓓蓓", age: 19 },
        { id: 3, name: "姜肖泰", age: 17 },
      ],
    };
  },
  components: {
    MyTable,
  },
  methods: {
    del(id) {
      this.list = this.list.filter((item) => item.id !== id);
    },
    fn(id, msg) {
      console.log(id, msg);
    },
  },
};
</script>

MyTable.vue

<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年纪</th>
        <th>操作</th>
      </tr>
    </thead>
    <!-- 
      四、插槽 - 作用域插槽
      作用域插槽: 定义 slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组件时可以用。
      基本使用步骤:
      1. 给 slot 标签, 以 添加属性的方式传值
      2. 所有添加的属性, 都会被收集到一个对象中
      3. 在template中, 通过 ` #插槽名= "obj" ` 接收,默认插槽名为 default
     -->
    <tbody>
      <!-- <tr>
        <td>1</td>
        <td>小张</td>
        <td>8</td>
        <td>
          <button>删除</button>
        </td>
      </tr> -->
      <tr v-for="(item, index) in data" :key="item.id">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <td>
          <!-- <button>删除</button> -->
          <slot :id="item.id" msg="普通数据"></slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: Array,
  },
};
</script>

<style scoped>
.my-table {
  width: 450px;
  text-align: center;
  border: 1px solid #ccc;
  font-size: 24px;
  margin: 30px auto;
}
.my-table thead {
  background-color: #1f74ff;
  color: #fff;
}
.my-table thead th {
  font-weight: normal;
}
.my-table thead tr {
  line-height: 40px;
}
.my-table th,
.my-table td {
  border-bottom: 1px solid #ccc;
  border-right: 1px solid #ccc;
}
.my-table td:last-child {
  border-right: none;
}
.my-table tr:last-child td {
  border-bottom: none;
}
.my-table button {
  width: 65px;
  height: 35px;
  font-size: 18px;
  border: 1px solid #ccc;
  outline: none;
  border-radius: 3px;
  cursor: pointer;
  background-color: #ffffff;
  margin-left: 5px;
}
</style>

4. 综合案例 - 商品列表

App.vue

<template>
  <!-- 06-src-商品列表 -->
  <!-- 
    需求说明:
    1. my-tag 标签组件封装
    (1) 双击显示输入框,输入框获取焦点
    (2) 失去焦点,隐藏输入框
    (3) 回显标签信息
    (4) 内容修改,回车 → 修改标签信息
    2. my-table 表格组件封装
    (1) 动态传递表格数据渲染
    (2) 表头支持用户自定义
    (3) 主体支持用户自定义
  -->
  <div class="table-case">
    <MyTable :data="goods">
      <template #head>
        <th>编号</th>
        <th>图片</th>
        <th>名称</th>
        <th width="100px">标签</th>
      </template>
      <!-- 拿到插槽传入的数据 解构 -->
      <template #con="{ item, index }">
        <td>{{ index + 1 }}</td>
        <td>
          <img :src="item.picture" />
        </td>
        <td>{{ item.name }}</td>
        <td>
          <MyTag v-model="item.tag"></MyTag>
        </td>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from "./components/MyTable.vue";
import MyTag from "./components/MyTag.vue";
export default {
  name: "TableCase",
  data() {
    return {
      goods: JSON.parse(localStorage.getItem("newGoods")) || [
        {
          id: 101,
          picture:
            "https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg",
          name: "梨皮朱泥三绝清代小品壶经典款紫砂壶",
          tag: "茶具",
        },
        {
          id: 102,
          picture:
            "https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg",
          name: "全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌",
          tag: "男鞋",
        },
        {
          id: 103,
          picture:
            "https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png",
          name: "毛茸茸小熊出没,儿童羊羔绒背心73-90cm",
          tag: "儿童服饰",
        },
        {
          id: 104,
          picture:
            "https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg",
          name: "基础百搭,儿童套头针织毛衣1-9岁",
          tag: "儿童服饰",
        },
      ],
    };
  },
  components: {
    MyTable,
    MyTag,
  },
  watch: {
    goods: {
      deep: true,
      handler(newGoods) {
        localStorage.setItem("newGoods", JSON.stringify(newGoods));
      },
    },
  },
};
</script>

<style lang="less" scoped>
.table-case {
  width: 1000px;
  margin: 50px auto;
  img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    vertical-align: middle;
  }
}
</style>

MyTable.vue

<template>
  <table class="my-table">
    <thead>
      <tr>
        <slot name="head"></slot>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in data" :key="item.id">
        <slot name="con" :item="item" :index="index"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
// import MyTag from "./MyTag.vue";
export default {
  components: {
    // MyTag,
  },
  props: {
    data: Array,
  },
};
</script>

<style  lang="less" scoped>
.my-table {
  width: 100%;
  border-spacing: 0;
  img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    vertical-align: middle;
  }
  th {
    background: #f5f5f5;
    border-bottom: 2px solid #069;
  }
  td {
    border-bottom: 1px dashed #ccc;
  }
  td,
  th {
    text-align: center;
    padding: 10px;
    transition: all 0.5s;
    &.red {
      color: red;
    }
  }
  .none {
    height: 100px;
    line-height: 100px;
    color: #999;
  }
}
</style>

MyTag.vue

<template>
  <div class="my-tag">
    <input
      class="input"
      type="text"
      placeholder="输入标签"
      v-if="isEdit"
      v-focus
      @blur="isEdit = false"
      @keyup.enter="changeTag"
      :value="value"
    />
    <!-- :value 是html标签自带的属性  "value" 是props数据 -->
    <!-- dbl - double, 双击 - dblclick -->
    <div class="text" v-else @dblclick="isEdit = true">{{ value }}</div>
  </div>
</template>

<script>
// 分类标签的功能:
// input和文字互斥 → 布尔数据 v-if
// 双击文字div 显示input标签 → 自动获得焦点
// input 要数据回显 - 父子通信
// v-model = value属性和input事件的缩写
// 用户输入后,回车 → 把新的内容渲染到页面 → 数据应该同步
export default {
  props: {
    value: String,
  },
  data() {
    return {
      isEdit: false,
    };
  },
  methods: {
    changeTag(e) {
      // 通知父组件保存用户输入的数据 $emit(事件名, 用户输入的数据)
      this.$emit("input", e.target.value);
      // 显示div,隐藏输入框
      this.isEdit = false;
    },
  },
};
</script>

<style  lang="less" scoped>
// lang="less" 表示的是less语法

.my-tag {
  cursor: pointer;
  .input {
    appearance: none;
    outline: none;
    border: 1px solid #ccc;
    width: 100px;
    height: 40px;
    box-sizing: border-box;
    padding: 10px;
    color: #666;
    &::placeholder {
      color: #666;
    }
  }
}
</style>

内容概要:本文档围绕水声网络(UAN)仿真中的信道建模展开,提供了基于Matlab的代码实现方案,旨在帮助科研人员构建和理解水声通信系统的仿真环境。文档不仅聚焦于信道建模的核心技术,还系统性地整合了智能优化算法、机器学习、图像处理、路径规划、无人机应用、信号处理、电力系统管理等多个前沿科研方向的技术方法与实际案例。涵盖的具体算法包括遗传算法、粒子群优化(PSO)、神经网络、卡尔曼滤波、支持向量机、极限学习机、卷积神经网络等,并深入探讨其在通信系统仿真中的应用。文档强调“借力”科研理念,倡导利用成熟的算法工具与仿真平台提升研究效率,并提供了丰富的配套资源,包括百度网盘链接和微信公众号入口,便于读者获取完整的代码、仿真模型及相关论文资料,全面支持科研复现与创新。; 适合人群:具备一定Matlab编程基础,从事通信工程、信号与信息处理、水声工程、自动化控制、电子信息、电力系统、无人机技术等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:① 学习并复现水声网络信道建模的Matlab仿真流程;② 探索智能优化算法、机器学习、路径规划等技术在通信与多学科交叉系统仿真中的集成应用;③ 获取可用于科研项目的高质量算法代码、仿真案例与复现资源,加速课题研究进程,提升科研创新能力与论文发表水平。; 阅读建议:建议读者按照文档推荐的主题顺序逐步学习,优先聚焦自身研究方向的相关内容,并结合网盘提供的完整资源进行代码实践、仿真调试与结果验证,以实现理论理解与动手能力的双重提升,充分发挥本资源在科研攻关中的支撑作用。
代码下载链接: https://pan.quark.cn/s/b27638adc362 在工业自动化监控领域中,WinCC 被视为一种常用的可视化软件,其功能在于构建人机界面(HMI)以及SCADA系统。本资料将阐释在WinCC环境下如何构建一个展现管道中流体流动的动态效果,此功能主要通过C动作脚本来完成。在此过程中,我们需要构建两个矩形,分别标记为 rec1 和 rec2。这两个矩形的高度相等,但 rec1 的宽度要小于 rec2。在具体实施时,rec1 将作为展示流体运动的可见单元,而 rec2 则作为辅助元素,尽管其“显示”属性被设为关闭,但在程序执行期间,其属性参数对流体动画的表现起着决定性作用,因为 rec2 与 rec1 的宽度差将决定流体每次移动的长度。 随后,我们需要将 rec1 和 rec2 整合为一个自定义对象。在自定义对象的属性配置中,应包含 rec1.Left、rec1.Width、rec1.Visible 和 rec2.Width 这些核心属性。这些属性将有助于在C脚本中精确控制对象的位置和可见状态,以达成流体运动的视觉效果。同时,用户可根据实际需求增加其他属性,以增强自定义对象的功能性。 在自定义对象的C脚本部分,我们设定了一个周期为“250ms”的触发器。该脚本的核心职责是计算并更新流体块的位置。借助 GetPropBOOL、GetPropWord 和 GetLeft 函数,我们可以获取对象的当前状态,涵盖其可见性、位置及宽度等参数。在循环操作中,流体块(rec1)将向右移动 rec2 - rec1 的距离,一旦流体块移出显示范围(即 rec1 的右边界达到或超过 rec2 的左边界),它将重新回到起始位置,从而形成流体持续流动的模...
内容概要:本文围绕同步电机与构网型变流器在低惯量电力系统中的频率稳定特性及多时间尺度动态交互机理展开深入研究,基于IEEE9节点混合拓扑结构,采用Simulink平台构建电磁暂态仿真模型,系统复现并对比了下垂控制、虚拟同步机控制(VSM)、匹配控制以及可调度虚拟振荡器控制(dVOC)等多种构网型变流器控制策略的动态响应特性与频率支撑能力。研究重点剖析了不同类型电源在系统扰动下的频率响应行为,揭示了同步机与变流器在惯性、阻尼及调节响应等不同时间尺度上的耦合机制与相互作用规律,旨在深化对高比例电力电子化电力系统稳定机理的理解,为新型电力系统的稳定分析、控制策略设计与运行优化提供坚实的理论依据与技术支撑。; 适合人群:具备电力系统分析、自动控制理论基础,熟悉Simulink仿真环境,从事新能源并网、微电网、构网型控制、电力系统稳定性研究的研究生、高校科研人员及电力行业工程技术人员。; 使用场景及目标:① 掌握基于IEEE9节点系统的低惯量电力系统建模与电磁暂态仿真方法;② 深入理解并对比主流构网型变流器控制策略的核心原理、实现方式及其对系统频率稳定的贡献差异;③ 分析同步电机与构网型变流器在故障扰动下的多时间尺度动态交互过程;④ 服务于相关领域的科研课题复现、高水平学术论文撰写、科技项目申报与技术方案论证。; 阅读建议:建议读者结合文中所述控制策略,亲手在Simulink中搭建或复现仿真模型,重点关注系统在负荷突变或短路故障等扰动工况下的频率响应曲线、功率动态过程及控制器内部变量变化,同时配合阅读相关领域的权威文献,以深刻理解控制机理与系统稳定性判据,从而有效提升科研实践与创新能力。
标题SpringBoot学生成绩信息管理系统安全设计与实现AI更换标题第1章引言介绍学生成绩信息管理系统安全设计的研究背景、意义、现状及论文方法与创新点。1.1研究背景与意义阐述学生成绩信息管理系统安全设计的重要性与必要性。1.2国内外研究现状分析国内外学生成绩信息管理系统安全设计的研究进展。1.3研究方法以及创新点概述本文的研究方法及在安全设计方面的创新点。第2章相关理论总结和评述学生成绩信息管理系统安全设计的相关理论。2.1信息安全基础理论阐述信息安全的基本概念、原则及重要性。2.2Web应用安全理论介绍Web应用安全漏洞、攻击手段及防御策略。2.3SpringBoot安全机制概述SpringBoot框架提供的安全机制及配置方法。第3章系统安全需求分析详细分析学生成绩信息管理系统的安全需求。3.1用户身份认证需求分析系统对用户身份认证的需求及安全要求。3.2数据传输安全需求阐述数据传输过程中的安全需求及加密技术。3.3数据存储安全需求介绍数据存储的安全需求及数据库安全策略。第4章系统安全设计详细介绍学生成绩信息管理系统的安全设计方案。4.1系统架构安全设计设计系统的整体架构,确保架构层面的安全性。4.2身份认证与授权设计设计用户身份认证和授权机制,确保用户合法访问。4.3数据传输与存储安全设计设计数据传输和存储的安全方案,保障数据安全性。第5章系统安全实现阐述学生成绩信息管理系统安全设计的具体实现过程。5.1开发环境与工具选择介绍系统开发所使用的环境和工具。5.2安全功能模块实现详细描述各安全功能模块的实现代码和逻辑。5.3系统测试与优化对系统进行安全测试,发现并修复潜在的安全漏洞。第6章研究结果呈现系统安全设计实现后的实验分析结果。6.1系统安全性能测试结果展示系统在不同安全测试场景下的性能表现。6.2安全漏洞修复情况介绍系统安全测试中发现并修复的安全漏洞情况。6.3
代码下载链接: https://pan.quark.cn/s/302bce6ed297 AutoHotkey(缩写为AHK)是一种功能强大的自动化脚本语言,特别适用于设计个性化快捷键和热键,以此来提升工作效率。这个“AHK便捷资源包”显然为入门者准备了一套综合性的材料,其中涵盖了多种便利的工具和详尽的解释,其目的在于减轻AHK的学习负担。 ** AHK的基础知识 ** AutoHotkey是一个开源的Windows自动化软件,它让用户能够通过编写脚本达成键盘与鼠标操作的自动化。AHK脚本能够生成热键、热字符串、模仿键盘和鼠标的输入、管理窗口以及执行更复杂的操作,例如文件处理、网络请求等。它的语法设计得简洁明了,让非技术背景的人也能轻易掌握。 ** 快捷键与热键 ** AHK的一项核心功能是构建快捷键或热键。用户可以通过设定特定的按键组合来执行特定的指令或脚本。比如,可以设定`Ctrl+Shift+S`来启动某个应用程序,或者使用`Alt+F4`来关闭当前正在使用的窗口,这些操作在日常工作中能显著提升效率。 ** 定制化工具 ** "AHK便捷资源包"里或许包含了一系列预先编写的AHK脚本,这些脚本可能是常用工具的简化版本,例如文本编辑器、计算器、截图软件等。它们已经配置了热键,用户可以直接运用,无需从头开始编写脚本。 ** 详尽的指南 ** 包内的“详尽指南”很可能收录了AHK的基础语法、函数、变量、流程控制等内容,对于初学者而言是非常有价值的参考资料。这些指南或许以教学形式呈现,指导用户如何构建自己的脚本,理解并运用AHK的各种特性。 ** 学习步骤推荐 ** 学习AHK时,首先应当掌握其基础语法,包含变量声明、函数调用、控制结构(例如if语句和循环)。接着,...
内容概要:本文系统介绍了基于热传导矩阵(HCM)的边缘检测方法在红外图像处理中的应用,并提供了完整的Matlab代码实现。该方法源于热传导模型的物理机制,通过模拟热量在图像像素间的扩散过程来识别边缘,特别适用于红外图像因噪声强、纹理弱、对比度低而导致传统边缘检测算子(如Sobel、Canny等)性能下降的场景。文章详细阐述了HCM方法的数学建模原理、数值求解流程、算法实现步骤以及在实际红外图像上的处理效果,充分展示了其在有效抑制噪声干扰的同时,仍能保持边缘连续性和完整性方面的显著优势,为复杂环境下红外图像的特征提取提供了新思路。; 适合人群:具备一定图像处理理论基础和Matlab编程能力的科研人员、工程技术人员及研究生,尤其适合从事红外成像、计算机视觉、遥感探测或模式识别等相关领域研究的专业人士。; 使用场景及目标:①作为红外图像预处理的关键环节,提升后续目标检测、识别与跟踪等任务的精度与鲁棒性;②为研究人员探索基于物理模型的新型图像处理算法提供理论参考与实践范例,推动热力学与图像科学的交叉融合创新。; 阅读建议:建议读者结合所提供的Matlab代码逐行解读算法实现细节,深入理解热传导方程的离散化过程与迭代求解策略,并在真实的红外图像数据集上进行实验验证与参数调优,以全面掌握HCM方法的性能特点、适用条件及其局限性。
内容概要:本文围绕“基于【闵可夫斯基和】的电动汽车可调能力评估研究”展开,提出了一种利用闵可夫斯基和数学工具对大规模电动汽车集群的可调能力进行建模与聚合的方法,通过Matlab代码实现相关算法,旨在量化电动汽车在参与电网调度中的灵活性潜力。研究通过构建单辆电动汽车在电池容量、充放电功率及用户出行行为等多重约束下的可行操作域,并运用闵可夫斯基和对海量个体可行域进行几何叠加,生成集群层面的整体可调能力包络,从而为电力系统调度提供精确、紧凑的需求侧资源表征。该方法有效提升了高比例新能源接入背景下电网对分布式灵活性资源的聚合与调控能力,增强了系统运行的稳定性与经济性。; 适合人群:具备电力系统优化、运筹学、凸几何分析基础及较强Matlab编程能力的研究生、高校科研人员及相关领域工程师,尤其适用于从事智能电网、车网互动(V2G)、需求侧管理与综合能源系统研究的专业人士。; 使用场景及目标:① 实现大规模电动汽车集群参与电网辅助服务(如削峰填谷、频率调节)的能力量化评估;② 支撑高波动性可再生能源电力系统的灵活性分析与安全经济调度决策;③ 为车网互动(V2G)项目的可行性论证、运营策略设计及聚合商商业模式构建提供核心技术支持。; 阅读建议:建议读者结合Matlab代码深入理解闵可夫斯基和在可行域建模与聚合中的具体实现细节,重点关注模型对电池动态、用户行为约束的数学刻画方式,并通过调整参数(如电池容量、出行时间分布)进行仿真实验,观察聚合结果的变化趋势,从而深刻掌握该方法的理论内涵与工程应用价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值