<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[微4e]]></title> 
<atom:link href="https://www.v4e.cn/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[为前端开发者搭建的交流家园]]></description>
<link>https://www.v4e.cn/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>vant-ui使用搜索框出现两个清除按钮</title>
    <link>https://www.v4e.cn/post-231.html</link>
    <description><![CDATA[<p><a href="https://www.v4e.cn/content/uploadfile/202603/ddf51773992760.png"><img src="https://www.v4e.cn/content/uploadfile/202603/thum-ddf51773992760.png" alt="" /></a></p>
<pre><code class="language-less"> //  隐藏浏览器原生 input[type=search] 的清除按钮，只保留 Vant 的
        :deep(.van-field__control[type="search"]::-webkit-search-cancel-button) {
          -webkit-appearance: none !important;
        }</code></pre>]]></description>
    <pubDate>Fri, 20 Mar 2026 15:45:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-231.html</guid>
</item>
<item>
    <title>VScode翻译软件</title>
    <link>https://www.v4e.cn/post-230.html</link>
    <description><![CDATA[<p>网址参考:<a href="https://blog.csdn.net/qq_57404736/article/details/147049359">https://blog.csdn.net/qq_57404736/article/details/147049359</a></p>
<p>1.申请<a href="http://fanyi-api.baidu.com/" title="百度开发者平台账号">百度开发者平台账号</a></p>
<p>2.往下翻，选择通用文本翻译<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/13f01757560937.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-13f01757560937.png" alt="" /></a></p>
<p>3.选择立即使用<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/1a931757561011.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-1a931757561011.png" alt="" /></a></p>
<p>4.申请完以后，右上角进入选择开发者信息<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/f32c1757561106.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-f32c1757561106.png" alt="" /></a></p>
<p>5.翻到下面，将自己要调用<a href="http://www.speedtest.cn" title="api(本地电脑)">api(本地电脑)</a>的外网IP粘贴进来<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/7c311757561312.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-7c311757561312.png" alt="" /></a></p>
<p>VScode设置<br />
1.组件默认的翻译是google，我们可以修改其他翻译源，以下是可以选择的百度翻译源<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/5d151757561576.png"><img src="https://www.v4e.cn/content/uploadfile/202509/5d151757561576.png" alt="" /></a></p>
<p>2.注意，也要在.vscode-&gt;settings.json中添加如下配置，如果少了哪个，注意添加下<br />
<a href="https://www.v4e.cn/content/uploadfile/202509/7a051757561825.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-7a051757561825.png" alt="" /></a></p>
<pre><code class="language-javascript">"i18n-ally.translate.engines": ["baidu"], // 翻译器，第一个不行会尝试后面的
// 插件将以何种语言作为基准来进行翻译相关的操作，如果你正在开发一个多语言应用，并且原始文案是用英语编写的，你可以将`sourceLanguage`设置为`en`（代表英语）。这样，插件就知道在提取文案进行翻译或者在代码中关联不同语言的文案时，以英语文案为原始参考。
"i18n-ally.translate.baidu.appid": "你在百度申请的appid", // 翻译器api-appid
"i18n-ally.translate.baidu.apiSecret": "你在百度申请的appSecret", // 翻译器api-Secret
"i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.keystyle": "nested", // 翻译路径格式 (翻译后变量格式 nested：嵌套式  flat:扁平式)
</code></pre>
<p>插件配置<br />
点击左侧i18n Ally菜单栏-&gt;翻译进度，可以看到中文(100%)，英文(0%)</p>
<p><a href="https://www.v4e.cn/content/uploadfile/202509/b7ba1757562123.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-b7ba1757562123.png" alt="" /></a></p>
<p><a href="https://www.v4e.cn/content/uploadfile/202509/29251757568775.png"><img src="https://www.v4e.cn/content/uploadfile/202509/thum-29251757568775.png" alt="" /></a></p>]]></description>
    <pubDate>Thu, 11 Sep 2025 11:13:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-230.html</guid>
</item>
<item>
    <title>图片转码(vue3)</title>
    <link>https://www.v4e.cn/post-229.html</link>
    <description><![CDATA[<p>项目要实现一个上传图片的功能，后端需要base64的格式。使用图片转base64格式，再发给后端，后端只需将转码结果存入数据库即可，前端调用接口获取到base64数据转码后的数据。再写入img src 标签即可。</p>
<h2>HTML部分</h2>
<pre><code class="language-html">&lt;el-form-item label="头像" prop="iconUrl"&gt;
            &lt;el-upload
              ref="upload"
              class="upload-demo"
              action=""
              :limit="1"
              :on-exceed="handleExceed"
              :auto-upload="false"
              list-type="picture"
              :file-list="fileList"
              :on-change="handleOnChange"
              :on-remove="handelOnRemove"
            &gt;
              &lt;template #trigger&gt;
                &lt;el-button type="primary"&gt;选择头像&lt;/el-button&gt;
              &lt;/template&gt;
            &lt;/el-upload&gt;
          &lt;/el-form-item&gt;</code></pre>
<h3>逻辑部分</h3>
<pre><code class="language-javascript">/**上传----------------------------start */
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import { genFileId } from 'element-plus';
const fileList = ref([]);
const fileType = ref(['png', 'jpg', 'jpeg']);
const upload = ref&lt;any&gt;();
const handleExceed: UploadProps['onExceed'] = (files) =&gt; {
  upload.value!.clearFiles();
  const file = files[0] as UploadRawFile;
  file.uid = genFileId();
  upload.value!.handleStart(file);
};

//图片验证
const handleOnChange = async (file: any) =&gt; {
  //选择图片验证
  if (!beforeAvatarUpload(file.raw)) {
    proxy?.$modal.msgError(`文件格式不正确, 请上传${fileType.value.join('/')}图片格式文件!`);
    upload.value!.clearFiles();
    return false;
  }
  form.value.iconUrl = await beforeUpload(file.raw);
};

// 上传之前的验证
const beforeAvatarUpload = (file: any) =&gt; {
  let isImg = false;
  if (fileType.value.length) {
    let fileExtension = '';
    if (file.name.lastIndexOf('.') &gt; -1) {
      fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
    }
    isImg = fileType.value.some((type: any) =&gt; {
      if (file.type.indexOf(type) &gt; -1) return true;
      if (fileExtension &amp;&amp; fileExtension.indexOf(type) &gt; -1) return true;
      return false;
    });
  } else {
    isImg = file.type.indexOf('image') &gt; -1;
  }
  return isImg;
  // if (!isImg) {
  //   proxy?.$modal.msgError(`文件格式不正确, 请上传${fileType.value.join('/')}图片格式文件!`);
  //   return false;
  // }
};

// 上传图片删除回调
const handelOnRemove = (file: any) =&gt; {
  fileList.value = [];
  form.value.iconUrl = '';
};

// 将文件流转为base64数据
const beforeUpload = (file: File) =&gt; {
  return new Promise&lt;string&gt;((resolve, reject) =&gt; {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =&gt; {
      const base64Data = (reader.result as string).split(',')[1]; // 去掉前缀
      resolve(base64Data); // 返回不带前缀的 Base64 字符串
    };
    reader.onerror = (error) =&gt; {
      reject(error);
    };
  });
};

// 将 Base64 数据转换为 Blob 对象
function base64ToBlob(base64: string): Blob {
  const byteStr = atob(base64);
  const u8Arr = new Uint8Array(byteStr.length);
  for (let i = 0; i &lt; byteStr.length; i++) {
    u8Arr[i] = byteStr.charCodeAt(i);
  }
  return new Blob([u8Arr]);
}

// 将 Blob 对象转换为 URL
const Base64ToUrl = (baseStr: any) =&gt; {
  const blob = base64ToBlob(baseStr);
  const url = URL.createObjectURL(blob);
  return url;
};
/**上传----------------------------end */</code></pre>
<h3>实现</h3>
<pre><code class="language-html"> &lt;el-table-column prop="" :label="$t('modelManager.iconUrl')"&gt;
            &lt;template #default="scope"&gt;
              &lt;img v-if="scope.row.iconUrl" :src="Base64ToUrl(scope.row.iconUrl)" style="width: 40px; height: 40px" alt="" /&gt;
              &lt;img v-else src="../../../assets/images/ai_models.png" style="width: 40px; height: 40px" alt="" /&gt;
            &lt;/template&gt;
 &lt;/el-table-column&gt;</code></pre>
<h3>编辑时的操作</h3>
<pre><code class="language-javascript">  fileList.value = [
      {
        name: '',
        url: Base64ToUrl(form.value.iconUrl) // 如果 iconUrl 为空，则使用默认路径
      }
    ];</code></pre>]]></description>
    <pubDate>Thu, 07 Aug 2025 15:11:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-229.html</guid>
</item>
<item>
    <title>vue3 前端 引入 markmap 思维导图，通过markdown解析成思维导图实现思路及方法</title>
    <link>https://www.v4e.cn/post-228.html</link>
    <description><![CDATA[<h2>1.下载插件</h2>
<p>npm i --save-dev @types/file-saver</p>
<p>npm install file-saver</p>
<p>npm install html-to-image</p>
<p>npm install markmap-lib</p>
<p>npm install markmap-view</p>
<p>npm install markmap-common</p>
<h2>2.创建页面</h2>
<pre><code class="language-html">&lt;template&gt;
  &lt;!-- 思维导图预览 --&gt;
  &lt;div class="mind"&gt;
    &lt;div class="controls"&gt;
      &lt;button class="btn" @click="zoomIn"&gt;放大&lt;/button&gt;
      &lt;button class="btn" @click="zoomOut"&gt;缩小&lt;/button&gt;
      &lt;button class="btn" @click="fitToScreen"&gt;适应屏幕&lt;/button&gt;
      &lt;button class="btn" @click="onSave"&gt;下载&lt;/button&gt;
    &lt;/div&gt;

    &lt;div class="svg-container"&gt;
      &lt;svg ref="svgRef"&gt;&lt;/svg&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
<pre><code class="language-javascript">&lt;script setup lang="ts"&gt;
import { ref, watch, onMounted, onUpdated } from 'vue';
import { Transformer } from 'markmap-lib';
import { Markmap } from 'markmap-view';
import * as htmlToImage from 'html-to-image';
import { saveAs } from 'file-saver';

const transformer = new Transformer();

// 存储 Markmap 实例和 SVG 引用
const mm = ref();
const svgRef = ref();

const props = defineProps({
  mapVla: {
    type: String, // 接收父组件传递的 mapVla 作为 prop
    default: () =&gt; {}
  }
});

// 放大和缩小功能
const zoomIn = () =&gt; mm.value?.rescale(1.25);
const zoomOut = () =&gt; mm.value?.rescale(0.8);

// 将思维导图适应屏幕
const fitToScreen = () =&gt; mm.value?.fit();

// 保存思维导图为图片
const onSave = async () =&gt; {
  if (svgRef.value) {
    const dataUrl = await htmlToImage.toPng(svgRef.value, {
      backgroundColor: 'white', // 设置下载时的背景颜色为白色
      style: {
        color: 'black' // 设置下载时的文字颜色为黑色
      }
    });
    saveAs(dataUrl, 'markmap.png'); // 保存为 PNG 文件
  }
};

// 根据传入的 initValue 更新思维导图
const update = (initValue: string) =&gt; {
  if (mm.value) {
    const { root } = transformer.transform(initValue);
    mm.value.setData(root);
    mm.value.fit();
  }
};

// 监听 mapVla 的变化，每当 mapVla 变化时重新更新思维导图
watch(
  () =&gt; props.mapVla,
  (newMapVla) =&gt; {
    update(newMapVla); // 更新思维导图
    fitToScreen();
  },
  { immediate: true, deep: true }
);

// 组件挂载时初始化思维导图
onMounted(() =&gt; {
  mm.value = Markmap.create(svgRef.value);
  update(props.mapVla); // 初次渲染时根据 mapVla 渲染思维导图
});

onUpdated(() =&gt; update(props.mapVla)); // 每当 mapVla 更新时重新渲染
&lt;/script&gt;</code></pre>
<pre><code class="language-scss">&lt;style scoped lang="scss"&gt;
.mind {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;

  .svg-container {
    flex: 1;
    svg {
      width: 100%;
      height: 100%;
    }
  }

  .controls {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 15px;
    margin-top: 10px;
    padding-right: 20px;
    .btn {
      padding: 10px 20px;
      font-size: 14px;
      background-color: #615ced;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      transition: all 0.3s ease;
    }

    .btn:hover {
      background-color: #4a52c3; /* 稍微偏蓝且比原来深一些 */
      transform: translateY(-2px);
    }

    .btn:active {
      background-color: #3b45a1; /* 更深的蓝色，增加差异 */
      transform: translateY(1px);
    }
  }
}
&lt;/style&gt;</code></pre>
<h2>3.引用组件</h2>
<pre><code class="language-html">   &lt;div class="chatMap" v-if="activeInput === '4'" :style="summaryStyle"&gt;
          &lt;Previewmap :mapVla="mapVla" /&gt;
   &lt;/div&gt;</code></pre>]]></description>
    <pubDate>Fri, 07 Feb 2025 14:08:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-228.html</guid>
</item>
<item>
    <title>el-form表单校验规则,使用于element、element-plus、ant-design-vue组件</title>
    <link>https://www.v4e.cn/post-227.html</link>
    <description><![CDATA[<h3>一、多个变量控制某一项校验。</h3>
<p><img src="https://www.v4e.cn/content/uploadfile/202411/f3cc1732871176.jpg" alt="" /></p>
<p>简单代码：重点在于prop=&quot;zdxx&quot;自定义绑定和自定义校验事件</p>
<pre><code class="language-html">    &lt;el-form v-loading="loading" ref="ruleFormRef" :model="formData" :rules="rules" label-width="160px"
      class="demo-ruleForm" :size="'default'" status-icon&gt;
      &lt;el-row :gutter="24"&gt;
        &lt;el-col :span="12"&gt;
          &lt;el-form-item label="云主机名称:" prop="virtualMachineName"&gt;
            &lt;el-input placeholder="请输入" clearable /&gt;
          &lt;/el-form-item&gt;
        &lt;/el-col&gt;

        &lt;el-col :span="24"&gt;
          &lt;el-form-item label="站点信息:" prop="zdxx"&gt;
            &lt;el-col :span="12"&gt;
              &lt;el-checkbox :disabled="formData.applyVirtualMachineType === 2"
                v-if="canEdit &amp;&amp; formData.applyVirtualMachineType !== 2" v-model="formData.supportSiteAFlag" label="主调"
                size="large" @change="changeCheckbox('siteAIp')" /&gt;
              &lt;el-checkbox disabled v-else v-model="formData.supportSiteAFlag" label="主调" size="large" /&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="12"&gt;
              &lt;el-checkbox :disabled="formData.applyVirtualMachineType === 2"
                v-if="canEdit &amp;&amp; formData.applyVirtualMachineType !== 2" v-model="formData.supportSiteBFlag" label="备调"
                size="large" @change="changeCheckbox('siteBIp')" /&gt;
              &lt;el-checkbox disabled v-else v-model="formData.supportSiteBFlag" label="备调" size="large" /&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="12"&gt;
              &lt;template v-if="formData.supportSiteAFlag"&gt;
                &lt;el-form-item prop="siteAIp"&gt;
                  &lt;el-input :disabled="formData.applyVirtualMachineType === 2"
                    v-if="canEdit &amp;&amp; formData.applyVirtualMachineType !== 2" v-model="formData.siteAIp" placeholder="请输入"
                    clearable /&gt;
                  &lt;span v-else&gt;主调IP：&lt;span class="ml"&gt;{{ formData.siteAIp }}&lt;/span&gt;&lt;/span&gt;
                &lt;/el-form-item&gt;
              &lt;/template&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="12"&gt;
              &lt;template v-if="formData.supportSiteBFlag"&gt;
                &lt;el-form-item prop="siteBIp"&gt;
                  &lt;el-input :disabled="formData.applyVirtualMachineType === 2"
                    v-if="canEdit &amp;&amp; formData.applyVirtualMachineType !== 2" v-model="formData.siteBIp" placeholder="请输入"
                    clearable /&gt;
                  &lt;span v-else&gt;备调IP：&lt;span class="ml"&gt;{{ formData.siteBIp }}&lt;/span&gt;&lt;/span&gt;
                &lt;/el-form-item&gt;
              &lt;/template&gt;
            &lt;/el-col&gt;
          &lt;/el-form-item&gt;
        &lt;/el-col&gt;
      &lt;/el-row&gt;
    &lt;/el-form&gt;
</code></pre>
<pre><code class="language-javascript">let formData = ref({
  virtualMachineName: undefined,
  // zdxx: [],
  supportSiteAFlag: false,
  siteAIp: undefined,
  supportSiteBFlag: false,
  siteBIp: undefined,
})

const isSite = (rule, value, callback) =&gt; {
  if (!formData.value.supportSiteAFlag &amp;&amp; !formData.value.supportSiteBFlag) {
    callback(new Error('请选择站点信息'))
  } else {
    callback()
  }
}

const rules = reactive({
  virtualMachineName: [{ required: true, message: '请输入云主机名称', trigger: 'blur' },],

  zdxx: [{ required: true, validator: isSite, trigger: 'change' },],
  // supportSiteAFlag: [{ required: true, validator: isSite, trigger: 'change' },],
  siteAIp: [{ validator: isIp1, trigger: 'blur' },],
  // supportSiteBFlag: [{ required: true, message: '请选择备调', trigger: 'change' },],
  siteBIp: [{ validator: isIp2, trigger: 'blur' },],
})
</code></pre>
<h3>二、新增和删除表单项，动态校验。</h3>
<p><img src="https://www.v4e.cn/content/uploadfile/202411/52061732871353.gif" alt="" /></p>
<p>以下代码可直接复制：重点在于:prop :rules :key。<br />
公式： 动态校验项的v-model的绑定值 = el-form的属性 :model的值 + '.' + :prop属性的值</p>
<p>根据公式倒推 :prop<br />
例如：v-model=“domain.premium” 就是 dynamicValidateForm.premiumList[index].premium ，而且:model=“dynamicValidateForm”<br />
所以得到 :prop = ‘premiumList.’ + index + ‘.premium’</p>
<h3>同时注意：直接在el-form-item上写动态的校验规则:rules=</h3>
<pre><code class="language-html">&lt;template&gt;
  &lt;div&gt;

    &lt;el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="110px" class="demo-dynamic"&gt;
      &lt;div class="bor_box" v-for="(domain, index) in dynamicValidateForm.premiumList" :key="domain.key"&gt;
        &lt;el-col :span="24"&gt;
          &lt;div class="box4_div"&gt;

            &lt;el-col :span="5"&gt;
              &lt;el-form-item label="保费：" :prop="'premiumList.' + index + '.premium'" :rules="{required: true, message: '保费不能为空', trigger: 'blur'}"&gt;
                &lt;el-input class="num_input" placeholder="请输入" v-model="domain.premium"&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="5"&gt;
              &lt;el-form-item label="保费率：" :prop="'premiumList.' + index + '.rate'" :rules="{required: true, message: '保费率不能为空', trigger: 'blur'}"&gt;
                &lt;el-input class="num_input" placeholder="请输入" :precision="2" :step="0.01" v-model="domain.rate"&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="5"&gt;
              &lt;el-form-item label="上游费用率：" :prop="'premiumList.' + index + '.feeRate'" :rules="{required: true, message: '上游费用率不能为空', trigger: 'blur'}"&gt;
                &lt;el-input class="num_input" placeholder="请输入" :max="100" v-model="domain.feeRate"&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
            &lt;el-col :span="5"&gt;
              &lt;el-form-item label="上游费用额：" :prop="'premiumList.' + index + '.fee'" :rules="{required: true, message: '上游费用额不能为空', trigger: 'blur'}"&gt;
                &lt;el-input class="num_input" placeholder="请输入" v-model="domain.fee"&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;

            &lt;el-col :span="4"&gt;
              &lt;el-button @click.prevent="removeDomain(domain,index)"&gt;删除&lt;/el-button&gt;
            &lt;/el-col&gt;

          &lt;/div&gt;
        &lt;/el-col&gt;

      &lt;/div&gt;
      &lt;div&gt;
        &lt;el-form-item&gt;
          &lt;el-button type="primary" @click="submitForm('dynamicValidateForm')"&gt;提交&lt;/el-button&gt;
          &lt;el-button @click="addDomain"&gt;添加保费&lt;/el-button&gt;
          &lt;el-button @click="resetForm('dynamicValidateForm')"&gt;重置&lt;/el-button&gt;
        &lt;/el-form-item&gt;

      &lt;/div&gt;

    &lt;/el-form&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<pre><code class="language-javascript">&lt;script&gt;
export default {
  data () {
    return {
      dynamicValidateForm: {
        premiumList: [{
          selected: 'Y', premium: undefined, rate: undefined, feeRate: undefined, fee: undefined, baofei1_unit: '', shangyoufeiyonge_unit: '', downRate: undefined, downFee: undefined, xiayoufeiyonge_unit: '',
        }],

      },
    }
  },
  created () {

  },
  methods: {
    submitForm (formName) {
      this.$refs[formName].validate((valid) =&gt; {
        if (valid) {
          alert('submit!')
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    resetForm (formName) {
      this.$refs[formName].resetFields()
    },
    removeDomain (item, index) {
      if (index !== 0) {
        this.dynamicValidateForm.premiumList.splice(index, 1)
      }
    },

    addDomain () {

      this.dynamicValidateForm.premiumList.push({
        selected: 'N', premium: undefined, rate: undefined, feeRate: undefined, fee: undefined, baofei1_unit: '', shangyoufeiyonge_unit: '', downRate: undefined, downFee: undefined, xiayoufeiyonge_unit: '',
        key: Date.now()
      })
    },
  },
}
&lt;/script&gt;
</code></pre>
<pre><code class="language-scss">&lt;style lang="less" scoped&gt;
.bor_box {
  margin-left: 50px;
  margin-bottom: 10px;

  overflow: hidden;
}
&lt;/style&gt;</code></pre>
<h3>三、el-form表单校验v-if不生效、el-form表单校验v-show不生效：</h3>
<p>例如有个表单项，通过控制显示两个不同的表单项内容，分别需要校验对应的表单项即可。</p>
<p>如果是直接用v-if，会发现切换时候，校验不生效；</p>
<p>如果直接使用v-show，又发现即使不切换，隐藏了另外的表单项，也会触发校验规则。</p>
<p>正确做法是：使用v-if控制表单项的显示隐藏，同时需要给el-form-item加上自己的 prop=&quot; &quot; 和 key=&quot; &quot; ，这样才能区分。当然:rules 也要有自己对应的提示。</p>
<p><img src="https://www.v4e.cn/content/uploadfile/202411/28c41732871561.gif" alt="" /></p>
<pre><code class="language-html">    &lt;template v-if="flag"&gt;
      &lt;el-form-item label="姓名：" prop="name" key="name" :rules="rules.name"&gt;
        &lt;el-input v-model="formData.name" placeholder=""&gt;&lt;/el-input&gt;
      &lt;/el-form-item&gt;
    &lt;/template&gt;
    &lt;template v-else&gt;
      &lt;el-form-item label="退回原因：" prop="desc" key="desc" :rules="rules.desc"&gt;
        &lt;el-input type="textarea" maxlength="500" show-word-limit v-model="formData.desc" placeholder="请填写退回原因"&gt;&lt;/el-input&gt;
      &lt;/el-form-item&gt;
    &lt;/template&gt;
</code></pre>]]></description>
    <pubDate>Fri, 29 Nov 2024 17:04:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-227.html</guid>
</item>
<item>
    <title>浏览器有哪几种缓存，各种缓存的优先级是什么样的？</title>
    <link>https://www.v4e.cn/post-225.html</link>
    <description><![CDATA[<h2>一、在浏览器中，常见的有四种缓存</h2>
<p>1、强制缓存</p>
<p>通过设置 Cache-Control 和 Expires 等响应头实现，可以让浏览器直接从本地缓存中读取资源而不发起请求。</p>
<p>2、协商缓存</p>
<p>通过设置 Last-Modified 和 ETag 等响应头实现，可以让浏览器发送条件请求，询问服务器是否有更新的资源。如果服务器返回 304 Not Modified 响应，则表示客户端本地缓存仍然有效，可直接使用缓存的资源。</p>
<p>3、Service Worker 缓存</p>
<p>Service Worker 是一种特殊的 JS 脚本，可以拦截网络请求并返回缓存的响应，以实现离线访问和更快的加载速度等功能。</p>
<p>4、Web Storage 缓存</p>
<p>包括 localStorage 和 sessionStorage。localStorage 用于存储用户在网站上的永久性数据，而 sessionStorage 则用于存储用户会话过程中的临时数据。</p>
<h2>二、常见缓存的优先级如下：（从高到低）</h2>
<p>① Service Worker 缓存</p>
<p>由于其可以完全控制网络请求，因此具有最高的优先级，即使是强制缓存也可以被它所覆盖。</p>
<p>② 强制缓存</p>
<p>如果存在强制缓存，并且缓存没有过期，则直接使用缓存，不需要向服务器发送请求。</p>
<p>③ 协商缓存</p>
<p>如果强制缓存未命中，但协商缓存可用，则会向服务器发送条件请求，询问资源是否更新。如果服务器返回 304 Not Modified 响应，则直接使用缓存。</p>
<p>④ Web Storage 缓存</p>
<p>Web Storage 缓存的优先级最低，只有在网络不可用或者其他缓存都未命中时才会生效。</p>]]></description>
    <pubDate>Wed, 27 Nov 2024 16:45:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-225.html</guid>
</item>
<item>
    <title>工具代码(持续更新)</title>
    <link>https://www.v4e.cn/post-224.html</link>
    <description><![CDATA[<h3>1.去除后端返回的html标签</h3>
<pre><code class="language-javascript"> // 去除后端返回的html标签
    stripHtml(html) {
      const tempDiv = document.createElement("div");
      tempDiv.innerHTML = html;
      return tempDiv.innerText || tempDiv.textContent || "";
    }</code></pre>
<p>使用:</p>
<pre><code class="language-html"> &lt;el-table-column prop="demandDescribe" :label="$t('project.public.remarks')" show-overflow-tooltip&gt;
          &lt;template slot-scope="scope"&gt;
            &lt;div v-text="stripHtml(scope.row.demandDescribe)"&gt;&lt;/div&gt;
            &lt;!-- &lt;span v-html="scope.row.demandDescribe"&gt;&lt;/span&gt; --&gt;
          &lt;/template&gt;
 &lt;/el-table-column&gt;</code></pre>
<h3>2.存储数据及取用数据</h3>
<p>最好写在utils下边,命名local.js</p>
<pre><code class="language-javascript">export default {
  // 获取本地存储的数据
  get(key) {
    return JSON.parse(localStorage.getItem(key));
  },

  // 设置本地存储中的数据
  set(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  },

  // 清除本地存储
  clear() {
    localStorage.clear();
  }
};
</code></pre>
<p>存储:</p>
<pre><code class="language-javascript">    godownClick(row) {
      if (row.headerIds.includes(String(this.nickID))) {
        local.set("GodownInfo", row); //存储
        this.$router.push({
          path: "/material/materials/goods-shelves" //跳转到货架管理
        });
      } else {
        this.$message.warning("你不是此仓库的负责人,无法查看该仓库 !");
        return;
      }
    },</code></pre>
<p>使用：</p>
<pre><code class="language-javascript">  mounted() {
    this.infoRow = local.get("GodownInfo"); //取用
  },</code></pre>
<h3>3.防抖</h3>
<pre><code class="language-javascript">/**
 * 防抖
 * @param {*} fn //一个需要进行防抖处理的原函数fn
 * @param {*} delay //延迟执行的时间delay（单位为毫秒）
 * @returns 
 * import { debounce, throttle } from "@/utils/yuUtils.js";
 *  debounce_sureDialogTransferClick: debounce(function (val) {
      this.sureDialogTransferClick(val);
    }, 800),
 */
export function debounce(fn, delay) {
  let timer = null;

  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() =&gt; {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}
</code></pre>
<p>使用：</p>
<pre><code class="language-javascript"> // 防抖(判断调用)
    debounce_onConfirm: debounce(function (type) {
      if (type === "save") {
        this.changeInfo();
      } else {
        this.onConfirm();
      }
      //this.onConfirm();
    }, 500),

      debounce_getComfirm: debounce(function () {
      this.getComfirm();
    }, 500),</code></pre>
<h3>4.节流</h3>
<pre><code class="language-javascript">/**
 * 节流
 * @param {*} fn //一个需要进行防抖处理的原函数fn
 * @param {*} delay //延迟执行的时间delay（单位为毫秒）
 * @returns
 */
export function throttle(fn, delay) {
  let canRun = true;

  return function (...args) {
    if (!canRun) return;

    canRun = false;
    setTimeout(() =&gt; {
      fn.apply(this, args);
      canRun = true;
    }, delay);
  };
}</code></pre>
<h3>5.el-input需要数值时不能为负数</h3>
<p>防止手输入负数</p>
<pre><code class="language-html">&lt;el-form-item label="数量" prop="amount"&gt;
 &lt;el-input v-model.number="Addform.amount" type="number" :min="1"&gt;
  &lt;!-- &lt;template slot="append"&gt;{{ $t('project.task.hour') }}&lt;/template&gt; --&gt;
  &lt;/el-input&gt;
&lt;/el-form-item&gt;

&lt;el-form-item label="单价" prop="unitPrice"&gt;
 &lt;el-input v-model="Addform.unitPrice"&gt;&lt;/el-input&gt;
&lt;/el-form-item&gt;</code></pre>
<pre><code class="language-javascript"> rules: {     
        amount: [
          { required: true, message: "请选择数量", trigger: "change" },
          { pattern: /^[0-9|^\\.]/, message: "数量不能为负数" }
        ],       
        unitPrice: [{ pattern: /^[0-9|^\\.]/, message: "单价不能为负数" }]
},</code></pre>
<h3>6.根据后端返回标签内的字段高亮并且在渲染是去除html标签</h3>
<pre><code class="language-javascript">// 处理 HTML 内容，先保留 &lt;em&gt; 标签内容背景色，再去除其他标签
const stripHtml = (html) =&gt; {
  // 创建一个临时容器
  const tempDiv = document.createElement('div');
  tempDiv.innerHTML = html;

  // 找到所有 &lt;em&gt; 标签并设置其样式
  const emTags = tempDiv.getElementsByTagName('em');
  Array.from(emTags).forEach((em) =&gt; {
    // 用 span 替代 &lt;em&gt; 标签并设置背景色
    const span = document.createElement('span');
    span.style.backgroundColor = '#eff4ff';
    span.style.padding = '2px';
    span.style.borderRadius = '4px';
    span.style.color = '#6698ff';
    // span.style.fontWeight = 'bold';
    span.innerHTML = em.innerHTML; // 将 &lt;em&gt; 标签中的内容放入 &lt;span&gt; 中
    em.replaceWith(span); // 替换 &lt;em&gt; 标签为 &lt;span&gt;
  });

  // 返回处理过的 HTML 内容（将其他标签移除，只保留纯文本和带样式的 &lt;span&gt;）
  return tempDiv.innerHTML;
};</code></pre>
<h3>7.table滚动加载(vue3)</h3>
<pre><code class="language-html"> &lt;el-table
            v-loading="tableLoading"
            ref="multipleTable"
            :data="tableData"
            tooltip-effect="dark"
            :highlight-current-row="true"
            :height="tableHeight"
            class="tableClass scrollTable"
            :class="[isTableTopMoreText ? 'table_top_more' : '']"
            :row-style="{ height: '50px' }"
            :header-row-style="{ height: '50px' }"
            :row-class-name="tableRowClassName"
            @selection-change="handleSelectionChange"
            @row-dblclick="handleRowDblClick"
            @row-click="handleRowClick"
            @row-contextmenu="rowContextmenuClick"
            @sort-change="changeTableSort"
          &gt;
    &lt;/el-table&gt;</code></pre>
<pre><code class="language-javascript">  const tableHeight = ref(0);

 onMounted(() =&gt; {
    apiFilesTreeQuery();
    tableHeight.value = window.innerHeight - 98;
    window.onresize = () =&gt; {
      //浏览器窗口变动的时候
      tableHeight.value = window.innerHeight - 98;
    };
  });

const scrollTableDom: any = ref('');

onMounted(() =&gt; {
  // 查找表格滚动区域的 DOM 元素，并将其赋值给 scrollTableDom
  scrollTableDom.value = document.querySelector('.scrollTable .el-table__body-wrapper .el-scrollbar__wrap');
  // 为该元素添加 'scroll' 事件监听器，滚动时调用 handleTableScroll 函数
  scrollTableDom.value.addEventListener('scroll', handleTableScroll);
});

onUnmounted(() =&gt; {
  // 在组件销毁时清除存储的 'expandedKeys' 数据
  sessionStorage.removeItem('expandedKeys');
  // 移除滚动事件监听器，避免内存泄漏
  scrollTableDom.value.removeEventListener('scroll', handleTableScroll);
});

//计算到最低端的滚动条位置(debounce 防抖方法)
const handleTableScroll = debounce((event) =&gt; {
  // 获取触发事件的目标 DOM 元素，即滚动容器
  const table = event.target;
  // 计算滚动条距离底部的距离
  const scrollPosition = table.scrollHeight - table.scrollTop - table.clientHeight;
  // console.log('🚀 ~ handleTableScroll ~ scrollPosition:', scrollPosition);

  // 如果滚动条距离底部小于等于 5 像素，表示用户已接近底部
  if (scrollPosition &lt;= 5) {
    apiTreeFileSelectTreePage('');
    // console.log(1234);
  }
}, 300);</code></pre>
<h3>7.el-autocomplete下拉加载更多(vue3)</h3>
<pre><code class="language-html">&lt;div class="page_vue1" v-else&gt;
      &lt;div class="slotClass"&gt;
        &lt;!-- v-scrollLoad="querySearchLoad" --&gt;
        &lt;!-- 搜索 --&gt;
        &lt;div class="searchBox"&gt;
          &lt;el-autocomplete
            ref="autocompleteRefs"
            v-model="searchInput"
            :fetch-suggestions="querySearch"
            placeholder="请输入搜索内容"
            class="searchInput"
            @select="handleSelectSearch"
          &gt;
            &lt;template #prefix&gt;
              &lt;el-icon&gt;&lt;Search /&gt;&lt;/el-icon&gt;
            &lt;/template&gt;

            &lt;template #default="{ item }"&gt;
              &lt;div class="content_box" v-infinite-scroll="querySearchLoad"&gt;
                &lt;div class="searchInput_name"&gt;
                  &lt;svg-icon :icon-class="selectSvgIcons(item)" style="font-size: 20px; vertical-align: middle; margin-right: 6px"&gt;&lt;/svg-icon&gt;
                  &lt;div&gt;{{ item.realName }}{{ item.suffix ? `.${item.suffix}` : '' }}&lt;/div&gt;
                &lt;/div&gt;
                &lt;div v-if="item.docContent" class="search_docContent" v-html="esSearchFunc(item.docContent)"&gt;&lt;/div&gt;
                &lt;div class="superiorsPath"&gt;
                  &lt;span&gt;路径:{{ item.superiorsPath }}&lt;/span&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/template&gt;
          &lt;/el-autocomplete&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;el-button link @click="closeClick"&gt;取消&lt;/el-button&gt;
    &lt;/div&gt;</code></pre>
<pre><code class="language-scss">.page_vue1 {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 48px;
  padding: 6px;
  border-bottom: 1px solid #eee;
}
.searchInput {
  height: 300px;
  border: none;
  width: 100%;
}
.content_box {
  width: 170px !important;
  border-bottom: 1px solid #efefef;
}
.searchInput_name {
  width: 100%;
  display: flex;
  align-items: center;

  div {
    // width: 10px;
    overflow: hidden;
    /*内容超出后隐藏*/
    text-overflow: ellipsis;
    /*超出内容显示为省略号*/
    white-space: nowrap;
    /*文本不进行换行*/
  }
}
.search_docContent {
  font-size: 12px;
  color: #939292;
  line-height: 22px;
}
.superiorsPath {
  font-size: 12px;
  color: #d6d6d6;
  line-height: 22px;
  text-align: right;
  overflow: hidden;
  /*内容超出后隐藏*/
  text-overflow: ellipsis;
  /*超出内容显示为省略号*/
  white-space: nowrap;
  /*文本不进行换行*/
}
</code></pre>
<p><img src="https://www.v4e.cn/content/uploadfile/202412/cdc61735195018.jpg" alt="" /></p>
<pre><code class="language-javascript">/**搜索----start */
const autocompleteRefs = ref();
const searchInput = ref('');
const searchStr = ref({
  page: 0,
  pageCount: null,
  list: []
});
const suggestions = ref([]); // 用来存储 suggestions 数据
const searchLoading = ref(false); //用于搜索的滚动加载

const selectedItem = ref(null);

const isSearchMore = ref(true);
/**搜索----end */

//搜索
const querySearch = (queryString, cb) =&gt; {
  if (searchInput.value == '' &amp;&amp; searchInput.value != undefined) {
    cb([]);
    return;
  }
  searchStr.value.page = 0;
  let oParam = {
    type: props.pageType,
    size: 20,
    realName: searchInput.value,
    page: searchStr.value.page
  };

  ApiPerselectPersonalTreePage(oParam).then((res: any) =&gt; {
    searchStr.value.page += 1;
    searchStr.value.pageCount = res.data.pageCount;
    searchStr.value.list = res.data.children;
    if (res.data.totalCount == 0) {
      ElMessage.warning('未匹配到数据,请重新输入!');
    }
    // 调用 callback 返回建议列表的数据
    cb(res.data.children);
  });
};

// 滚动加载
const querySearchLoad = debounce(() =&gt; {
  if (searchStr.value.page &gt; searchStr.value.pageCount) {
    //   this.divider = true;
    return;
  }

  if (!searchLoading.value) {
    searchLoading.value = true;

    let oParam = {
      type: props.pageType,
      size: 20,
      realName: searchInput.value,
      page: searchStr.value.page
    };
    ApiPerselectPersonalTreePage(oParam).then((res: any) =&gt; {
      searchLoading.value = false;
      searchStr.value.page += 1;
      searchStr.value.pageCount = res.data.pageCount;
      autocompleteRefs.value.suggestions = autocompleteRefs.value.suggestions.concat(res.data.children); //加载出来的数据放到suggestions中
    });
  }
}, 300);</code></pre>
<p><img src="https://www.v4e.cn/content/uploadfile/202412/f3cc1735195051.jpg" alt="" /></p>]]></description>
    <pubDate>Wed, 20 Nov 2024 15:36:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-224.html</guid>
</item>
<item>
    <title>el-input的失焦事件及el-table的移入移出事件</title>
    <link>https://www.v4e.cn/post-223.html</link>
    <description><![CDATA[<p>设置了:row-class-name=&quot;tableRowClassName&quot; ，添加index和 @row-click=&quot;cellClick&quot; 行点击事件<br />
表格部分添加 row-key=&quot;boxId&quot;,需要唯一值</p>
<pre><code class="language-html"> &lt;el-table :data="outData" style="width: 100%" @expand-change="expandChange"&gt;
          &lt;el-table-column type="expand"&gt;
            &lt;template slot-scope="scope"&gt;
              &lt;el-table
                style="padding: 0px; width: 100%"
                :data="sonData"
                row-key="boxId"
                @cell-click="cellClick"
                :row-class-name="tableRowClassName"
                @cell-mouse-enter="hoverRow"
                @cell-mouse-leave="leaveRow"&gt;
                &lt;el-table-column prop="suppliesName" label="物料名称"&gt;&lt;/el-table-column&gt;
                &lt;el-table-column prop="validTime" label="过期时间" show-overflow-tooltip&gt; &lt;/el-table-column&gt;
                &lt;el-table-column prop="boxName" label="货架位置"&gt;&lt;/el-table-column&gt;
                &lt;el-table-column prop="quantity" label="数量"&gt;
                  &lt;template slot-scope="scope"&gt;
                    &lt;div style="display: flex;align-items: center"&gt;
                      &lt;span&gt;{{ scope.row.quantity }}&lt;/span&gt;
                    &lt;/div&gt;
                  &lt;/template&gt;
                &lt;/el-table-column&gt;

                &lt;el-table-column prop="inputVal" label="出库数量" width="180"&gt;
                  &lt;template slot-scope="scope"&gt;
                    &lt;div style="display: flex; align-items: center" @click="scope.row.isShow = true"&gt;
                      &lt;!-- 领取数显示 --&gt;
                      &lt;span v-if="!scope.row.isShow &amp;&amp; scope.row.appliedQuantity &gt; 0" style="margin-left: 5px;"&gt;
                        领取数 : {{ scope.row.appliedQuantity }}
                      &lt;/span&gt;

                      &lt;div class="div_input" v-show="scope.row.showDeleteIcon"&gt;&lt;/div&gt;

                      &lt;!-- 输入框 --&gt;
                      &lt;el-input
                        type="Number"
                        ref="sortNumRef"
                        v-if="scope.row.isShow"
                        controls-position="right"
                        @input="handleInputChange(scope.row)"
                        style="width:110px; margin-left: 8px;"
                        v-model="scope.row.inputVal"
                        @blur="handleBlur(scope.row)"&gt;
                      &lt;/el-input&gt;
                    &lt;/div&gt;
                  &lt;/template&gt;
                &lt;/el-table-column&gt;
              &lt;/el-table&gt;
            &lt;/template&gt;
          &lt;/el-table-column&gt;

          &lt;el-table-column prop="materialName" label="物料名称" show-overflow-tooltip&gt; &lt;/el-table-column&gt;
          &lt;el-table-column prop="amount" label="剩余数量" show-overflow-tooltip&gt; &lt;/el-table-column&gt;
          &lt;el-table-column prop="quantity" label="申请数量" show-overflow-tooltip&gt; &lt;/el-table-column&gt;
          &lt;el-table-column prop="roomArea" label="状态" show-overflow-tooltip&gt;
            &lt;template slot-scope="scope"&gt;
              &lt;span&gt;{{ scope.row.outboundStatus === '1' ? '未领取' : scope.row.outboundStatus === '2' ? '已领取' : '' }}&lt;/span&gt;
              &lt;!-- &lt;p v-if="scope.row.outboundStatus=='1'"&gt;未领取&lt;/p&gt;
              &lt;p v-if="scope.row.outboundStatus=='2'"&gt;已领取&lt;/p&gt; --&gt;
            &lt;/template&gt;
          &lt;/el-table-column&gt;
        &lt;/el-table&gt;</code></pre>
<p>这里主要采用v-show来控制显示与隐藏</p>
<pre><code class="language-html">  &lt;div class="div_input" v-show="scope.row.showDeleteIcon"&gt;&lt;/div&gt;</code></pre>
<p>获取到数据时遍历，添加showDeleteIcon来控制视图的显示</p>
<pre><code class="language-javascript">    // 展开事件----动态获取内嵌表数据
    expandChange(row, expandedRows) {
      let oParam = {
        materialId: row.materialId
      };
      if (expandedRows.length &gt; 0) {
        ApiRecordQuantity(oParam).then((res) =&gt; {
          if (res.code === "200") {
            // 处理子表数据，增加 isShow 和 inputVal 字段
            this.sonData = res.rows.map((item) =&gt; ({
              ...item,
              isShow: false, // 默认隐藏输入框
              inputVal: 0, // 默认数量为 0，用户可以编辑
              materialId: oParam.materialId, // 将 materialId 传递到 sonData 中
              showDeleteIcon: false // 默认不显示删除图标
            }));
            // console.log("🚀 ~ this.sonData=res.rows.map ~ sonData:", this.sonData);

            // this.calculateTotalVal(row);
          }
        });
      }
    },</code></pre>
<h2>悬浮在某行时，显示内容</h2>
<p>主要增加了两个事件@cell-mouse-enter=&quot;hoverRow&quot;移入事件和@cell-mouse-leave=&quot;leaveRow&quot;移出事件</p>
<pre><code class="language-html"> &lt;el-table
style="padding: 0px; width: 100%"
:data="sonData"
row-key="boxId"
@cell-click="cellClick"
:row-class-name="tableRowClassName"
@cell-mouse-enter="hoverRow"
@cell-mouse-leave="leaveRow"&gt;</code></pre>
<p>中间同样采用showDeleteIcon来控制其显示和隐藏</p>
<p>移入移出的话就相对简单了，只需要将移入的视图更新即可<br />
别的不需要进行更新</p>
<pre><code class="language-javascript"> tableRowClassName({ row, rowIndex }) {
      //把每一行的索引放进row
      row.index = rowIndex;
    },

    // 移入效果
    hoverRow(row) {
      if (row.isShow == true || row.appliedQuantity &gt; 0) {
        row.showDeleteIcon = false;
      } else {
        row.showDeleteIcon = true; //显示
        this.$set(this.sonData, row.index, row); //更新数据
      }
    },

    // 移出效果
    leaveRow(row) {
      row.showDeleteIcon = false;
      this.$set(this.sonData, row.index, row);
    },</code></pre>
<p><img src="https://www.v4e.cn/content/uploadfile/202411/cdc61731986947.jpg" alt="" /></p>
<h1>input失焦事件:</h1>
<pre><code class="language-html"> &lt;el-input
type="Number"
ref="sortNumRef"
v-if="scope.row.isShow"
 controls-position="right"
@input="handleInputChange(scope.row)"
style="width:110px; margin-left: 8px;"
v-model="scope.row.inputVal"
@blur="handleBlur(scope.row)"&gt;
&lt;/el-input&gt;</code></pre>
<pre><code class="language-javascript">//点击表格单前行
    cellClick(row) {
      //聚焦input
      this.$nextTick(() =&gt; {
       this.$refs.sortNumRef.$refs.input.focus();
      });

      row.showDeleteIcon = false;
      this.$set(this.sonData, row.index, row);
      row.isShow = true; // 隐藏输入框
    },

    // 输入时处理
    handleInputChange(row) {
      const minValue = 0; // 最小值
      const maxValue = row.quantity; // 最大值（根据 `scope.row.quantity`）

      // 如果输入值小于最小值，设置为最小值
      if (row.inputVal &lt; minValue) {
        row.inputVal = minValue;
      }
      // 如果输入值大于最大值，设置为最大值
      if (row.inputVal &gt; maxValue) {
        row.inputVal = maxValue;
      }
    },

    //input失焦处理
    handleBlur(row) {
      row.showDeleteIcon = false;
      this.$set(this.sonData, row.index, row);
      row.isShow = false;
      if (row.inputVal &gt; 0) {
        row.appliedQuantity = row.inputVal; // 设置已申请数量
        row.outboundStatus = "2"; // 更新状态为已领取
        //row.isShow = false; // 隐藏输入框
      }
    }</code></pre>
<p><img src="https://www.v4e.cn/content/uploadfile/202411/f3cc1731987105.jpg" alt="" /></p>]]></description>
    <pubDate>Tue, 19 Nov 2024 11:18:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-223.html</guid>
</item>
<item>
    <title>form表单页面修改及保存</title>
    <link>https://www.v4e.cn/post-222.html</link>
    <description><![CDATA[<p>进入页面是全部为禁用,点击修改时解除禁用表单,最后保存信息</p>
<pre><code class="language-html">    &lt;div class="header-top"&gt;
      &lt;span class="header-title"&gt;基本设置&lt;/span&gt;

      &lt;el-button :type="saveButtonType" @click="toggleEdit('Editform')"&gt;
        {{ Editdisabled ? '修改信息' :'保存信息' }}
      &lt;/el-button&gt;
    &lt;/div&gt;
    &lt;!-- &lt;el-row :gutter="gutter"&gt;
          &lt;el-col :span="24"&gt;
            &lt;el-form-item label="负责人" prop="headers"&gt;
              &lt;div class="userstyle"&gt;
                &lt;div class="name" v-if="Editform.headers"&gt;
                  &lt;span class="notice-scope-user" v-for="(v,i) in Editform.headerName" :key="i"&gt;
                    {{ v.slice(0,1) }}
                  &lt;/span&gt;
                &lt;/div&gt;

                &lt;span class="notice-scope-add" @click="openSelectUser(Editform.headers)"&gt;
                  &lt;svg-icon icon-class="tianjiayonghu"&gt;&lt;/svg-icon&gt;
                &lt;/span&gt;
              &lt;/div&gt;
            &lt;/el-form-item&gt;
          &lt;/el-col&gt;
        &lt;/el-row&gt; --&gt;

    &lt;div&gt;
      &lt;el-form :disabled="Editdisabled" :model="Editform" ref="Editform" :rules="Editrules" label-position="top"&gt;
        &lt;div class="Addform_div"&gt;
          &lt;el-row :gutter="gutter"&gt;
            &lt;el-col :span="24"&gt;
              &lt;el-form-item label="库房名称" prop="name"&gt;
                &lt;el-input v-model="Editform.name" maxlength="15" placeholder="请输入库房名称" show-word-limit&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
          &lt;/el-row&gt;

          &lt;el-row :gutter="gutter"&gt;
            &lt;el-col :span="12"&gt;
              &lt;el-form-item label="库房编号" prop="number"&gt;
                &lt;el-input v-model="Editform.number" maxlength="15" placeholder="请输入库房编号" show-word-limit&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;

            &lt;el-col :span="12"&gt;
              &lt;el-form-item label="区域" prop="roomArea"&gt;
                &lt;el-input v-model="Editform.roomArea" maxlength="15" placeholder="请输入区域" show-word-limit&gt;&lt;/el-input&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
          &lt;/el-row&gt;

          &lt;el-row&gt;
            &lt;el-col&gt;
              &lt;el-form-item label="备注"&gt;
                &lt;div class="Editor_div"&gt;
                  &lt;myEditor style="width: 100%;" ref="RemarksRefs" :contenteditable="contenteditable" /&gt;
                &lt;/div&gt;
              &lt;/el-form-item&gt;
            &lt;/el-col&gt;
          &lt;/el-row&gt;
        &lt;/div&gt;
      &lt;/el-form&gt;
    &lt;/div&gt;</code></pre>
<pre><code class="language-javascript">data() {
    return {
      Editdisabled: true,
      contenteditable: false,
      gutter: 14,
      Editform: {
        // headers: "",
        // headerName: [],
        roomArea: "",
        name: "",
        number: "",
        roomRemarks: ""
      },
      saveButtonType: "primary", //保存（修改）type颜色

    };
  },

  methods: {
    // 修改信息或保存信息
    toggleEdit(formName) {
      if (this.Editdisabled) {
        this.Editdisabled = false;
        this.contenteditable = true;
        this.saveButtonType = "success";
      } else {
        this.$refs[formName].validate((valid) =&gt; {
          if (valid) {
            this.contenteditable = false;
            this.Editdisabled = true;
            this.saveButtonType = "primary";
            this.addFileBox(); //保存方法
            //调用页面数据刷新
            // this.$EventBus.$emit("yzp_getRoomAll_Refresh");
          } else {
            return false;
          }
        });
      }
    },

    //保存信息
    addFileBox() {
      let oParam = {
        id: this.infoRow.id,
        name: this.Editform.name,
        number: this.Editform.number,
        // headerIds: this.Editform.headers,
        roomArea: this.Editform.roomArea,
        roomArea: this.Editform.roomArea,
        roomRemarks: this.$refs.RemarksRefs.getContent() //获取富文本内容
      };

      ApiroomUpdate(oParam).then((res) =&gt; {
        if (res.code == "200") {
          this.$message.success("编辑成功 !");
        }
      });
    },</code></pre>]]></description>
    <pubDate>Mon, 11 Nov 2024 09:33:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-222.html</guid>
</item>
<item>
    <title>后端返回数据是带着html标签,渲染时不需要</title>
    <link>https://www.v4e.cn/post-221.html</link>
    <description><![CDATA[<p>返回数据如下:<br />
<img src="https://www.v4e.cn/content/uploadfile/202410/cdc61730280292.jpg" alt="" /></p>
<p>解决如下:</p>
<pre><code class="language-javascript"> methods: {
   // 去除html标签
    stripHtml(html) {
      const tempDiv = document.createElement("div");
      tempDiv.innerHTML = html;
      return tempDiv.innerText || tempDiv.textContent || "";
    },
   },</code></pre>
<p>使用:</p>
<pre><code class="language-html">    &lt;div class="custom-supplies" v-text="stripHtml(scope.row.suppliesDetails)"&gt;&lt;/div&gt;</code></pre>]]></description>
    <pubDate>Wed, 30 Oct 2024 17:24:00 +0800</pubDate>
    <dc:creator>yang</dc:creator>
    <guid>https://www.v4e.cn/post-221.html</guid>
</item></channel>
</rss>