vue - 问题汇总

  • 单元格合并

function resetSpanArr() {
  let contactDot = 0;
  state.spanArr = [];
  state.drawerForm.socTenantDTOList.forEach((item, index) => {
    //遍历tableData数据,给spanArr存入一个1,第二个item.id和前一个item.id是否相同,相同就给
    //spanArr前一位加1,spanArr再存入0,因为spanArr为n的项表示n项合并,为0的项表示该项不显示,后面有spanArr打印结果
    if (index === 0) {
      state.spanArr.push(1);
    } else {
      if (item.tenantId === state.drawerForm.socTenantDTOList[index - 1].tenantId) {
        state.spanArr[contactDot] += 1;
        state.spanArr.push(0);
      } else {
        contactDot = index;
        state.spanArr.push(1);
      }
    }
  });
}
function objectSpanMethod({ row, column, rowIndex, columnIndex }) {
  console.log(rowIndex, columnIndex);

  if (columnIndex === 0 || columnIndex === 1) {
    const _row = state.spanArr[rowIndex];
    const _col = _row > 0 ? 1 : 0;
    //该形式为行合并
    return {
      rowspan: _row,
      colspan: _col,
    };
  }
}
  • vue3封装弹框组件-函数声明方式和命令式
https://juejin.cn/post/7377578894591000586
https://juejin.cn/post/7223775785206792229?from=search-suggest


import { h, createApp, nextTick, provide, ref } from 'vue';
import MyMessageBox from './MyMessageBox.vue';
let messageBoxInstance = null;

const QWMessageBox = (options) => {
  return new Promise((resolve, reject) => {
    if (!messageBoxInstance) {
      const div = document.createElement('div');
      document.body.appendChild(div);

      const visible = ref(false);

      const app = createApp({
        setup() {
          return () =>
            h(MyMessageBox, {
              ...options,
              visible,
              'onUpdate:visible': (value) => {
                visible.value = value;
                if (!value) {
                  setTimeout(() => {
                    app.unmount();
                    div.remove();
                    messageBoxInstance = null;
                  }, 300); // 等待动画完成
                }
              },
              onConfirm: () => {
                resolve();
              },
              onCancel: () => {
                // resolve(false);
                console.log('用户点击了取消');
              },
            });
        },
      });
      app.mount(div);
      messageBoxInstance = { app, visible };
    }
    nextTick(() => {
      messageBoxInstance.visible.value = true;
    });
  });
};

export default QWMessageBox;

// import { createApp } from 'vue';

// import Dialog from './MyMessageBox1.vue';

// function openModal({ title, content, confirmBtnTxt = '确定', cancelBtnTxt = '取消' }) {
//   return new Promise((resolve, reject) => {
//     const app = createApp(Dialog, {
//       title,
//       content,
//       confirmBtnTxt,
//       cancelBtnTxt,
//       onConfirm: () => {
//         unmount();
//         resolve();
//       },
//       onCancel: () => {
//         unmount();
//         // reject();
//       },
//     });

//     // 创建一个挂载容器
//     const parentNode = document.createElement('div');
//     document.body.appendChild(parentNode);

//     // 卸载组件
//     const unmount = () => {
//       app.unmount();
//       document.body.removeChild(parentNode);
//     };

//     // 挂载组件
//     app.mount(parentNode);
//   });
// }

// export default openModal;

<template>
  <div class="message-box-delete">
    <el-dialog v-model="dialogVisible" width="450" @close="dialogVisible = false">
      <template #header>
        <div class="my-header">
          <div class="left">
            <el-icon v-if="type == 'danger'" :size="24" style="height: 100%">
              <SvgIcon :iconClass="'error'" />
            </el-icon>
            <img v-else src="@/assets/images/command/waring.png" alt="" />

            <span class="text-medium" style="font-size: 20px; font-weight: 600">删除</span>
          </div>
        </div>
      </template>

      <slot>
        <div>
          <span class="content text-regular"
            >您确认要删除<span class="text-semibold">{{ title }}</span
            >吗?</span
          >
        </div>
        <span v-if="content && content?.length > 0" class="content text-regular">{{ content }}</span>
      </slot>

      <template #footer>
        <el-button :disabled="isCancelDisabled" @click="onCancel">取消</el-button>
        <el-button :disabled="isConfirmDisabled" type="primary" @click="onConfirm"> 确定 </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ElButton, ElDialog, ElIcon } from 'element-plus';
import SvgIcon from '@/components/SvgIcon/index';

const emit = defineEmits(['update:visible', 'confirm', 'cancel']);

const props = defineProps({
  visible: Boolean,
  title: String,
  type: String, // 警告(waring) 还是错误(danger)状态
  isConfirmDisabled: Boolean,
  isCancelDisabled: Boolean,
  content: String,
});

const dialogVisible = computed({
  get() {
    return props.visible;
  },
  set(v) {
    emit('update:visible', v);
  },
});
const onConfirm = () => {
  dialogVisible.value = false;
  emit('confirm');
};
const onCancel = () => {
  dialogVisible.value = false;
  emit('cancel');
};
</script>

<style lang="scss">
.message-box-delete {
  .my-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    .left {
      display: flex;
      align-items: center;
      gap: 8px;
    }
  }
  .content {
    margin-bottom: 24px;
    display: inline-block;
  }

  .el-overlay-dialog {
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .el-dialog {
    margin-top: 0 !important;
  }
  .el-dialog__close {
    color: #666 !important;
  }
}
</style>

  • 时间区间选择器,控制时间区间
 <el-date-picker
            v-model="state.searchForm.date"
            type="datetimerange"
            :default-time="defaultTimeRange"
            value-format="YYYY-MM-DD HH:mm:ss"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            :disabled-date="disabledDate"
            @calendar-change="calendarChange"
            :clearable="true"
          />



function calendarChange(e) {
  state.minDate = e[0].getTime();
}
//时间筛选范围一个月
function disabledDate(time) {
  return (
    time.getTime() <= moment(state.minDate).subtract(6, 'month').valueOf() ||
    time.getTime() >= moment(state.minDate).add(6, 'month').valueOf()
  );
}
  • 居中布局
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  • 自定义指令
export default function (app) {
  app.directive("special-chars", {
    mounted(el, binding, vnode) {
      el.addEventListener("input", function (event) {
        // 获取输入的值
        const value = event.target.value;
        // 使用正则表达式检测反斜杠(根据需要匹配相应限制字符)
        const regex =
          /[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g;
        // 如果输入值包含反斜杠,则替换为空格
        if (regex.test(value)) {
          // 使用 replace 方法替换反斜杠为空格
          const newValue = value.replace(regex, "");
          // 将新值设置回输入框
          event.target.value = newValue;
          // 触发 @input 事件,使其更新组件中的数据
          //   vnode.componentInstance.$emit("input", newValue);
          // 触发input事件来更新绑定的v-model值
          el.dispatchEvent(new Event("input", { bubbles: true }));
        }
      });
    },
    unmounted(el) {
        el.removeEventListener('input');
      }
  });
}

  • 下载通知
// 下载视频
    downLoad(item, tab) {
      this.downLoadIndex++
      const notify = this.$notify({
        title: 'Work start download',
        message: `<div id='notify-${this.downLoadIndex}'>The video is downloading, progress is 0%, please wait</div>`,
        dangerouslyUseHTMLString: true,
        type: 'success',
        duration: 0,
      })

      this.downLoadFile(item, this.downLoadIndex, tab)
        .then(() => {
          notify.close()
          this.$notify({
            title: 'Work download completed',
            type: 'success',
            duration: 3000,
          })
        })
        .catch(() => {
          notify.close()
          this.$notify({
            title: 'Work download failed',
            type: 'error',
            duration: 3000,
          })
        })
    },
    downLoadFile(item, index, tab) {
      let url = ''
      if (tab == 'avatar') {
        url = item.audioUrl
      } else {
        url = item.videoUrl
      }
      let name = item.workName + '_Anylang-ai'

      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open('GET', url, true)
        xhr.send()
        xhr.responseType = 'blob'
        xhr.addEventListener('progress', (ev) => {
          const percent = parseInt((ev.loaded / ev.total) * 100)
          const dom = document.getElementById(`notify-${index}`)
          dom.innerText = `The video is downloading, progress is ${percent}%, please wait`
        })
        xhr.onload = (ev) => {
          if (xhr.status == 200) {
            const urlx = window.URL.createObjectURL(xhr.response)
            const dom = document.createElement('a')
            dom.href = urlx
            dom.download = name
            dom.click()
            URL.revokeObjectURL(dom.href)
            resolve()
          } else if (response.status == 404) {
            reject()
          } else if (response.status == 500) {
            reject()
          }
        }
      })
    },
  • 分享一些::after和::before使用的经验

&:before画圆


<template>
    <div>
         <div class="title">今日新增量</div >
    <div/>
<template>
<style>
title {
          position: relative;
          &:before {
          // top: 5px;
          // left: -15px;
          position: absolute;
          display: block;
          width: 10px;
          height: 10px;
          border-radius: 50%;
          background: #fa6861;
          content: '';
          }
    }
<style/>

&:before竖条

<template>
    <div>
         <div class="title">前面加个竖条</div >
    <div/>
<template>
<style>
 .title {
        position: relative;
        &:before {
          top: 25px;
          left: 80px;
          position: absolute;
          display: block;
          width: 6px;
          height: 30px;
          background: #4d99f9;
          content: '';
        }
    }
<style/>

&:before插入图片

<template>
    <div>
         <div class="title">前面加个图片</div >
    <div/>
<template>
<style>
.title {
        position: relative;
        &:before {
          content: '';
          position: absolute;
          left: 75px;
          top: 26px;
          height: 28px;
          width: 12px;
          background: url('~@/assets/images/second-title.png') no-repeat;
          background-size: 100% 100%;
        }
    }
<style/>
  • 一行文字过多时,省略号隐藏显示

1.页面部分
<el-row>
  <el-col :span="24">
    <div class="information-title">
     一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;
     一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;
     一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;一行文字过多时,隐藏省略显示;
    </div>
  </el-col>
</el-row>
2.css样式部分
.information-title {
  color: #19d3ea;
  font-size: 18px;
  width: 100%;   /*一定要设置宽度,或者元素内含的百分比*/
  overflow:hidden; /*溢出的部分隐藏*/
  white-space: nowrap; /*文本不换行*/
  text-overflow:ellipsis;/*ellipsis:文本溢出显示省略号(...);clip:不显示省略标记(...),而是简单的裁切*/
  • 防止被压缩
flex-shrink: 0;
  • vue3 使用类似.sync的功能:
// 父组件
  <child-modal v-model:visible="visible"></child-modal>
// 子组件
<template>
  <a-modal v-model:visible="show" title="Basic Modal" @ok="handleOk">
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </a-modal>
</template>

<script setup>
import { computed, defineProps, defineEmits} from 'vue'
const props = defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
})
const $emit = defineEmits(['update:visible'])
const show = computed({
  get() {
    return props.visible
  },
  set(v) {
    $emit('update:visible', v)
  },
})
const handleOk = e => {
  console.log(e)
  show.value = false
}
</script>

  • Promise

Promise有三种状态,分别是“待定”、“已完成”和“已拒绝”。当一个Promise被创建时,它处于“待定”状态。当操作成功完成时,Promise进入“已完成”状态,并返回一个包含操作结果的值。当操作失败时,Promise进入“已拒绝”状态,并返回一个包含错误信息的原因。

Promise可以链式调用,这使得它们可以更方便地组合异步操作。Promise还可以通过catch()方法来处理错误,这使得代码的错误处理更加容易。

链式调用(实现同步操作):
下面直接return值,是因为Promise会自动用Promise包裹


image.png
  • 列表网格排列:


    image.png
// 进度条随机值
    addIntervalProgress() {
      //取[10, 20]之间的随机整数
      this.currentProgress = Math.floor(Math.random() * (20 - 10 + 1)) + 10;
      // console.log("1111 ---- ", this.currentProgress);
      let timer = setInterval(() => {
        this.currentProgress += Math.floor(Math.random() * 5);
        // console.log("2222 ---- ", this.currentProgress);

        if (this.currentProgress > 99) {
          this.currentProgress = 99;
          clearInterval(timer);
        }
      }, 100);
    },
  • H5 播放rtsp视频流
    前段目前不支持直接播放这种格式,一般都是通过把rtsp擦、转成前段支持的格式
    1、https://cloud.tencent.com/developer/article/1805057
    VLC是一个不错的Chrome播放rtsp视频流方案,延迟低,性能稳定。但是从2015年以后,Chrome等浏览器取消对NPAPI支持后,VLC就不能直接使用了

  • mac可以用node.js的多版本管理器n来升级和切换,命令如下:

1、sudo npm cache clean -f //清除node.js的cache

2、sudo npm install -g n //使用npm安装n模块

3、npm view node versions // 查看node所有版本

4、sudo n latest // 升级到最新版本

      sudo n stable // 升级到稳定版本

      sudo n xx.xx // 升级到具体版本号

5、node -v  //查看当前安装的版本号

6、n   //检查目前安装了哪些版本的node,会出现已安装的node版本,选一个就可以直接切换了
  • 全局禁用空格

app.directive('space', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    el.addEventListener('input', (event) => {
      // 阻止空格字符的输入
      if (event.target.value?.includes(' ')) {
        // 移除空格,并更新输入框的值
        event.target.value = event.target.value.replace(/\s+/g, '');
        // 如果你需要触发 input 事件的更新(比如用于 v-model)
        // 可以使用 Vue 的 nextTick 来确保 DOM 更新后再触发事件
        setTimeout(() => {
          const events = new Event('input', { bubbles: true });
          el.dispatchEvent(events);
        }, 50);
      }
    });
  },
});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容