Skip to content

30K 前端笔试题,需要纯手写(不能用电脑)

一、CSS 有几种方法实现垂直水平居中?请写出来。(css 基础)

Details

CSS 中实现元素的水平和垂直居中有多种方式,下面我会详细介绍几种常用的方法:

1. Flexbox(弹性盒模型)

Flexbox 是一个非常强大的布局工具,可以轻松实现水平和垂直居中。

css
.parent {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
}

2. Grid(网格布局)

Grid 布局也是一个强大的布局系统,可以实现复杂的布局设计,包括居中对齐。

css
.parent {
  display: grid;
  place-items: center; /* 水平和垂直居中 */
}

3. 绝对定位和负边距

通过绝对定位元素到父容器的中心,然后使用负边距把它移动到确切的中心位置。

css
.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

4. 绝对定位与 margin:auto

利用绝对定位和自动边距结合,也能实现居中效果。

css
.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  width: 100px; /* 或其他宽度 */
  height: 100px; /* 或其他高度 */
}

5. 行内元素的文本居中

对于行内元素或文本,可以使用text-alignline-height属性实现水平和垂直居中。

css
.parent {
  text-align: center;
  height: 100px; /* 确定高度 */
  line-height: 100px; /* 与高度相同以垂直居中文本 */
}

6. 使用表格布局

虽然不推荐使用表格布局进行页面布局,但在某些情况下,它可以用来实现居中。

css
.parent {
  display: table-cell;
  text-align: center;
  vertical-align: middle;
}

7. 伪元素垂直居中法

使用vertical-align: middle;配合一个伪元素来实现行内元素或行内块元素的垂直居中。这种方法是基于伪元素的垂直对齐行为,通常用于行内元素(如文本或图片)与其相邻的伪元素相对于父元素的垂直居中。

这种方法的关键是在父元素内创建一个伪元素(通常使用::before::after),这个伪元素的高度设为 100%,并设置vertical-align: middle;,这样伪元素就会扩展到父元素的高度并垂直居中。然后,行内元素或行内块元素通过相同的vertical-align: middle;属性与这个伪元素对齐,从而实现垂直居中。

下面是一个示例:

html
<div class="parent">
  <span class="child">我是文本</span>
</div>
css
.parent {
  line-height: 100px; /* 增加行高以便观察垂直居中效果 */
  border: 1px solid #000; /* 边框仅用于可视化 */
}

.parent::before {
  content: "";
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  margin-right: -0.25em; /* 用于调整可能出现的间隙 */
}

.child {
  display: inline-block;
  vertical-align: middle;
}

在这个示例中,.parent的伪元素::before通过设置display: inline-block;height: 100%;来扩展到与.parent相同的高度,并通过vertical-align: middle;实现垂直居中。.child(即实际的行内元素或行内块元素)也使用vertical-align: middle;与伪元素对齐,从而达到垂直居中的效果。

这种方法特别适合于需要在不改变 HTML 结构的情况下对小块内容(如图标、文本或小图片)进行垂直居中的场景。它利用了 CSS 伪元素和vertical-align属性的特性,提供了一种相对简单而有效的解决方案。


这些方法各有利弊,适用于不同的场景。Flexbox 和 Grid 布局是现代布局的首选方法,因为它们更灵活,更容易适应各种屏幕大小和设备。绝对定位方法虽然有效,但可能需要更多的调整来确保响应式设计。选择哪种方法取决于具体的项目需求和兼容性考虑。

二、 var a='a123',b='b234’,请在不调用其他变量的情况下,互换 a,b 的值。(js 基础)

答案:

Details

在 JavaScript 中,你可以使用解构赋值(Destructuring assignment)来在不引入其他变量的情况下互换两个变量的值。这是 ES6 及以后版本中提供的一个特性。对于你给出的例子,操作如下:

javascript
let a = "a123",
  b = "b234";

// 使用解构赋值互换变量的值
[a, b] = [b, a];

console.log(a); // 输出: b234
console.log(b); // 输出: a123

这段代码首先定义了两个变量ab,然后通过解构赋值一行代码实现了ab值的互换。这种方法简洁且不需要引入任何额外的变量。


在 JavaScript 中,除了使用解构赋值之外,还有其他几种方法可以在不引入新变量的情况下交换两个变量的值。下面是几种方法:

  1. 使用加减运算(仅适用于数字): 这种方法只适用于数值类型的交换,由于你的例子是字符串,这种方法不适用。但为了完整性,我还是简要说明一下:

    javascript
    var a = 5,
      b = 10;
    a = a + b;
    b = a - b; // 现在b是原始的a值
    a = a - b; // 现在a是原始的b值
  2. 使用位运算符(仅适用于数字): 同样,这也只适用于数值类型。这种方法通过位运算来交换两个数值,而不需要使用额外的空间:

    javascript
    var a = 5,
      b = 10;
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
  3. 利用字符串操作(适用于你的情况): 对于字符串或者更通用的情况,可以使用字符串的拼接和切割方法来交换两个变量的值。不过,这种方法实际上在内部可能仍然会使用一些隐藏的变量或者临时空间来实现操作,所以它可能违反了你的“不调用其他变量”的要求。但从代码层面看,没有显式使用新变量:

    javascript
    var a = "a123",
      b = "b234";
    a = a + b;
    b = a.substring(0, a.length - b.length);
    a = a.substring(b.length);

总的来说,解构赋值是交换变量最简洁和现代的方法,特别是对于非数值类型的变量。其他方法或者涉及特定类型的操作,或者在实现上不如解构赋值优雅。在实际编程中,建议根据具体情况选择最适合的方法。

三、请写出下列函数的执行顺序,并说出原因。(es6 promise 使用基础)

javascript
const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve("success");
  console.log(2);
  setTimeout(() => {
    console.log(3);
  });
});
promise.then((res) => {
  console.log(res);
});

console.log(5);

答案:

Details

在解释这个代码段的执行顺序和原因时,首先需要理解 JavaScript 的事件循环和任务队列的概念,特别是区分宏任务(macrotasks)和微任务(microtasks)。宏任务包括 setTimeout、setInterval 等,而 Promise 回调则属于微任务。JavaScript 的事件循环首先执行所有的同步代码,然后执行微任务队列中的任务,最后才执行宏任务队列中的任务。根据这个原理,我们来看看上述代码的执行顺序:

  1. 首先,new Promise 的执行器(executor function)立即执行,因此 console.log(1); 是第一个打印出来的,输出 1
  2. 接着,在执行器内部,resolve("success");promise 的状态设置为已解决(resolved),但是这不会立即执行 .then 方法绑定的回调,而是将它放入微任务队列等待当前同步代码执行完毕。
  3. 紧接着,console.log(2); 同步执行,输出 2
  4. setTimeout 内的代码是一个宏任务,它会被放入宏任务队列,等待当前执行栈清空以及当前微任务队列执行完毕后才会执行。
  5. 然后,执行器代码执行完毕,事件循环继续,遇到 console.log(5);,这是同步代码,立即执行,输出 5
  6. 现在,同步代码执行完毕,事件循环会检查微任务队列。发现 .then 方法的回调函数,执行它,输出 "success"
  7. 最后,事件循环会处理宏任务队列中的任务。执行 setTimeout 中的 console.log(3);,输出 3

因此,整个代码的输出顺序和解释如下:

  1. 首先执行同步代码,输出 12
  2. 执行同步 console.log(5);,输出 5
  3. 执行微任务队列中的任务,输出 "success"
  4. 执行宏任务队列中的任务,输出 3

所以,最终的执行顺序和输出是:

bash
1
2
5
"success"
3

这正是因为 JavaScript 先执行同步任务,然后是微任务,最后是宏任务。

四、请写出下列代码的执行结果,并说出原因。(需要区分 http 请求执行成功或失败)(js 中级异步处理)

javascript
const test = () => {
  return appApi
    .aesEncode("123")
    .then((res) => {
      console.log(res);
    })
    .catch((err) => {
      console.log(err);
    });
};

test()
  .then((res) => {
    console.log("res");
  })
  .catch(() => {
    console.log("error");
  });

答案:

Details

要理解这段代码的执行结果及其原因,我们需要考虑几个关键点,包括 Promise 的工作原理以及thencatch如何处理异步操作的成功与失败。

这段代码定义了一个test函数,它返回一个调用appApi.aesEncode("123")的 Promise。这个 Promise 首先尝试加密字符串"123",然后根据操作的成功与否执行thencatch块。

  1. 如果appApi.aesEncode("123")成功执行,它的结果将被传递给then中的函数,打印出加密后的字符串。之后,因为then中没有返回一个新的 Promise,所以test()返回的 Promise 被解析为undefined,并且这导致外部的then块被执行,打印出"res"。

  2. 如果appApi.aesEncode("123")执行失败,它的错误将被传递给catch中的函数,打印出错误信息。与then块类似,如果catch块中没有返回一个新的 Promise,test()返回的 Promise 也会被解析为undefined,最终导致外部的then块被执行,打印出"res"。

不过,需要注意的是,如果thencatch块中返回了一个新的 Promise,那么外部的thencatch将基于这个新返回的 Promise 的状态被解析。

综上所述,不管appApi.aesEncode("123")执行成功还是失败,最终都会打印出"res",因为外部的then总会被执行,前提是thencatch块中没有返回新的 Promise 导致状态改变。至于appApi.aesEncode("123")的结果,根据其执行成功或失败,将打印加密后的字符串或错误信息。

这个分析基于thencatch块没有改变返回的 Promise 状态的假设。如果thencatch块返回了另一个状态为拒绝(rejected)的 Promise,那么外部的catch将会被执行,打印出"error"。

五、判断如下数组中是否存在相同的元素。(js 中级数据结构与数组处理)

输入:[1,2,3,4]=> false 输入:[1,2,3,2,1]= >true

答案:

Details

1. 使用 Set 数据结构

要判断一个数组中是否存在相同的元素,你可以使用 JavaScript 的几种方法。其中一种简单有效的方法是使用 Set 数据结构,它只能存储唯一的值。你可以比较原始数组和转换为 Set 后的数组的长度。如果长度不同,说明原数组中有重复的元素。以下是如何实现的示例代码:

javascript
function hasDuplicate(arr) {
  return new Set(arr).size !== arr.length;
}

// 测试示例
console.log(hasDuplicate([1, 2, 3, 4])); // 应该返回 false
console.log(hasDuplicate([1, 2, 3, 2, 1])); // 应该返回 true

这段代码首先定义了一个hasDuplicate函数,它接收一个数组arr作为参数。然后,它通过创建一个新的 Set 实例(new Set(arr))并比较这个 Set 的size属性和原数组的length属性来检查数组中是否有重复的元素。如果size不等于length,意味着数组中有重复的元素,函数返回true;否则,返回false。最后,使用两个测试示例来验证这个函数的行为。

2. 使用indexOflastIndexOf

javascript
function hasDuplicateUsingIndex(arr) {
  for (let i = 0; i < arr.length; i++) {
    if (arr.indexOf(arr[i]) !== arr.lastIndexOf(arr[i])) {
      return true; // 发现重复元素
    }
  }
  return false; // 未发现重复元素
}

这种方法遍历数组中的每个元素,并使用indexOflastIndexOf方法查找该元素在数组中的第一次和最后一次出现的索引。如果这两个索引不相同,说明至少有两个相同的元素。

3. 使用排序

javascript
function hasDuplicateUsingSort(arr) {
  arr.sort(); // 排序数组
  for (let i = 0; i < arr.length - 1; i++) {
    if (arr[i] === arr[i + 1]) {
      return true; // 发现相邻的重复元素
    }
  }
  return false; // 未发现重复元素
}

这种方法首先对数组进行排序,这样所有重复的元素就会被排列在一起。然后,遍历排序后的数组,检查是否有相邻的元素相同。如果发现相同,则返回true

4. 使用forEach与临时对象

javascript
function hasDuplicateUsingObject(arr) {
  const tempObj = {};
  let hasDup = false;
  arr.forEach((element) => {
    if (tempObj[element]) {
      hasDup = true; // 发现重复元素
    } else {
      tempObj[element] = true; // 标记元素出现
    }
  });
  return hasDup;
}

这种方法使用一个临时对象来记录数组中每个元素的出现。遍历数组中的每个元素,如果在临时对象中已经有该元素的记录,则说明发现了重复元素;否则,将该元素加入到临时对象中。这种方法在处理大数组时相比前两种方法通常有更好的性能。

以上是检查数组中是否存在重复元素的几种方法。每种方法有其适用场景,你可以根据实际需要选择最适合的方法。

六、使用面向对象的思想实现一个类,他能满足如下功能;(js 中级面向对象)

javascript
LazyMan("Tony");
// Hi I am Tony

LazyMan("Tony").sleep(10).eat("lunch");
// Hi I am Tony
// 等待了 10 秒...
// I am eating lunch

LazyMan("Tony").eat("lunch").sleep(10).eat("dinner");
// Hi I am Tony
// I am eating lunch
// 等待了 10秒..
// I am eating dinner

LazyMan("Tony")
  .eat("lunch")
  .eat("dinner")
  .sleepFirst(5)
  .sleep(10)
  .eat("junk food");
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了 10秒....
// I am eating junk food

答案:

Details
javascript
class LazyManClass {
  constructor(name) {
    this.name = name;
    this.queue = [];
    console.log(`Hi I am ${name}`);
    setTimeout(() => {
      this.next();
    }, 0);
  }

  next() {
    if (this.queue.length > 0) {
      const firstTask = this.queue.shift();
      firstTask();
    }
  }

  sleep(time) {
    this.queue.push(() => {
      setTimeout(() => {
        console.log(`等待了 ${time} 秒...`);
        this.next();
      }, time * 1000);
    });
    return this;
  }

  eat(food) {
    this.queue.push(() => {
      console.log(`I am eating ${food}`);
      this.next();
    });
    return this;
  }

  sleepFirst(time) {
    this.queue.unshift(() => {
      setTimeout(() => {
        console.log(`等待了 ${time} 秒...`);
        this.next();
      }, time * 1000);
    });
    return this;
  }
}

function LazyMan(name) {
  return new LazyManClass(name);
}

七、请用你熟悉的前端框架根据该题目描述实现一个动态配置化表单组件,需要接收表单配置。(开放题,说出思路即可)

根据给出下述基础结构动态生成的表单,要求必须输入手机号与所对应的用户名 name,可根据需要重新定义或增加属性

javascript
const list = [
  { type: "select", options: [1, 2, 3], field: "company", label: "公司" },
  { label: "姓名", type: "input", field: "name" },
  { label: "日期", type: "date", field: "time" },
  { label: "手机号", type: "input", field: "phone" },
];

答案:

Details

在 Vue 中,我们可以创建一个动态表单组件,该组件接受一个表单配置数组作为 props,然后使用v-for指令遍历这个数组来动态生成表单元素。我们还可以使用 Vue 的v-model来实现双向数据绑定,并利用计算属性或观察者来实现验证逻辑(如手机号和用户名的必填验证)。以下是我可能采取的步骤:

  1. 定义组件 Props

    • 接收一个名为formConfig的 props,它是一个对象数组,每个对象描述了一个表单元素。
  2. 模板渲染

    • 使用v-for指令在模板中迭代formConfig,并根据元素类型决定如何渲染每个表单元素。
  3. 数据绑定

    • 对于每个表单项,使用v-model将输入绑定到动态创建的表单数据模型上。
  4. 表单验证

    • 为手机号和用户名输入字段实现必填验证。
    • 可以使用 Vue 的内置指令如v-ifv-show来显示验证错误消息。
  5. 样式与布局

    • 根据需要,使用 CSS 来设计和布局表单元素。
  6. 提交逻辑

    • 实现一个提交方法,当表单验证通过时,触发一个事件,将表单数据发送到父组件或 API。

具体实现示例如下:

vue
<template>
  <form @submit.prevent="handleSubmit">
    <div v-for="field in formConfig" :key="field.field">
      <label :for="field.field">{{ field.label }}</label>
      <component
        :is="field.type === 'select' ? 'select' : 'input'"
        v-model="formData[field.field]"
        :type="field.type !== 'select' ? field.type : null"
        :options="field.options"
        :id="field.field"
        :name="field.field"
        v-if="field.type !== 'date'"
      >
        <option
          v-if="field.type === 'select'"
          v-for="option in field.options"
          :key="option"
          :value="option"
        >
          {{ option }}
        </option>
      </component>
      <input
        v-else
        type="date"
        v-model="formData[field.field]"
        :id="field.field"
        :name="field.field"
      />
      <span v-if="!isValid(field.field) && wasValidated"
        >This field is required</span
      >
    </div>
    <button type="submit">Submit</button>
  </form>
</template>

<script>
export default {
  props: {
    formConfig: {
      type: Array,
      required: true,
    },
  },
  data() {
    const formData = {};
    this.formConfig.forEach((field) => {
      formData[field.field] = "";
    });
    return {
      formData,
      wasValidated: false,
    };
  },
  methods: {
    handleSubmit() {
      this.wasValidated = true;
      if (this.isFormValid) {
        this.$emit("submit", this.formData);
      }
    },
    isValid(field) {
      // Implement validation logic here
      return this.formData[field] !== "";
    },
  },
  computed: {
    isFormValid() {
      return this.formConfig.every((field) => this.isValid(field.field));
    },
  },
};
</script>

在这个示例中,每个表单元素都根据其type来决定渲染为input还是select。我们还添加了一个名为isValid的方法,用于检查字段是否已填写。在提交表单之前会调用isValid来确认所有必填字段都已正确填写。如果表单有效,则handleSubmit会触发一个submit事件,父组件可以监听这个事件来获取表单数据。

八、请写出身份证号的正则验证

Details

在 JavaScript 中,你可以使用正则表达式来匹配中国大陆的身份证号码。这里提供一个匹配 15 位和 18 位身份证的正则表达式示例:

javascript
// 正则表达式匹配中国大陆的身份证号码(15位或18位)
const idCardRegex =
  /^(^[1-9]\d{5}\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{2}$)|(^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{2}([0-9Xx])$)$/;

// 测试身份证号码
const testIdCard = (idCard) => idCardRegex.test(idCard);

// 使用示例
console.log(testIdCard("11010519491231002X")); // 18位身份证号
console.log(testIdCard("110105491231002")); // 15位身份证号

这个正则表达式idCardRegex解释如下:

  • ^[1-9]:身份证号以 1-9 中的一个数字开始,排除所有地区编码以 0 开始的情况。
  • \d{5}:接下来是 5 个数字,表示区县代码和出生顺序码。
  • \d{2}:然后是 2 个数字,表示年份的最后两位(仅限于 15 位身份证)。
  • ((0[1-9])|(1[0-2])):接下来是一个月份,01-12。
  • (([0-2][1-9])|10|20|30|31):接着是一个日期,01-31。
  • \d{2}$:最后是 2 个数字的顺序码(仅限于 15 位身份证)。

对于 18 位身份证号的额外部分:

  • (19|20):表示世纪,目前身份证只包含 19 和 20 两个世纪。
  • \d{2}:这是完整的四位年份。
  • ([0-9Xx])$:最后一位可以是 0-9 之间的任何数字,或者是字母 X/x(校验码)。

函数testIdCard使用这个正则表达式来检测传入的身份证号是否符合格式。

九、请将扁平数组转换成具有层级结构的对象数组。

[{id:1, pid:0}, {id:2, pid:1}] => [{id:1, pid:0, children:[{id:2, pid:1}]}]
Details

答案

在 JavaScript 中,可以通过使用递归或迭代的方式将扁平数组转换成层级结构的对象数组。这里有一种实现方式,我们可以创建一个查找表来映射id到对应的节点,并构建一个树形结构:

javascript
function buildHierarchy(arr) {
  const nodes = {};
  const rootNodes = [];

  // 创建一个节点查找表
  arr.forEach((item) => {
    nodes[item.id] = { ...item, children: [] };
  });

  // 构建树结构
  arr.forEach((item) => {
    const id = item.id;
    const pid = item.pid;
    const node = nodes[id];

    if (node.pid === 0) {
      rootNodes.push(node); // 如果pid是0,意味着它是根节点
    } else {
      if (nodes[pid]) {
        // 如果可以在nodes中找到对应的pid,则为父节点
        nodes[pid].children.push(node);
      }
    }
  });

  return rootNodes;
}

const flatArray = [
  { id: 1, pid: 0 },
  { id: 2, pid: 1 },
];
const hierarchicalArray = buildHierarchy(flatArray);
console.log(hierarchicalArray);

这段代码首先遍历数组,创建了一个节点的映射表。之后再次遍历数组,根据每个节点的pid将其放到对应父节点的children数组中。如果一个节点的pid为 0,则表示它是根节点。最终返回包含根节点和其子节点的数组。

十、现有以下场景,用户提供地市列表,以及对应的机构信息,开发一个页面提供地市级联选择器,当用户选中某一个地市的时候,快速返回该地市对应的机构信息。 请用 JS 完成编码实现。

地市列表数据如下:

json
[
  {
    "code": "11",
    "name": "北京市",
    "children": [
      {
        "code": "1101",
        "name": "市辖区",
        "children": [
          { "code": "110101", "name": "东城区" },
          { "code": "110102", "name": "西城区" }
        ]
      }
    ]
  },
  {
    "code": "12",
    "name": "天津市",
    "children": [
      {
        "code": "1201",
        "name": "市辖区",
        "children": [
          { "code": "120101", "name": "和平区" },
          { "code": "120102", "name": "河东区" },
          { "code": "120103", "name": "河西区" }
        ]
      }
    ]
  }
]

机构信息 数据如下(数据量为一万条):

json
[
  { "code": "120101", "id": "12010101", "name": "Placeholder for name 1" },
  { "code": "120101", "id": "12010102", "name": "Placeholder for name 2" },
  { "code": "120102", "id": "12010201", "name": "Placeholder for name 3" },
  { "code": "120102", "id": "12010202", "name": "Placeholder for name 4" },
  { "code": "120103", "id": "12010301", "name": "Placeholder for name 5" },
  { "code": "120103", "id": "12010302", "name": "Placeholder for name 6" },
  { "code": "110101", "id": "11010101", "name": "Placeholder for name 7" },
  { "code": "110101", "id": "11010102", "name": "Placeholder for name 8" }
]

效果:

该地市没有对应的机构信息。

答案:

Details

html 代码

html
<!DOCTYPE html>
<html>
  <head>
    <title>地市机构信息选择器</title>
  </head>
  <body>
    <select id="citySelector">
      <!-- 地市选项将通过JS动态加载 -->
    </select>

    <div id="institutionInfo">
      <!-- 机构信息将显示在这里 -->
    </div>

    <script>
      // 假定这些数据是通过某种方式加载的,这里直接使用给定的数据作为示例
      const citiesData = [
        {
          code: "11",
          name: "北京市",
          children: [
            {
              code: "1101",
              name: "市辖区",
              children: [
                { code: "110101", name: "东城区" },
                { code: "110102", name: "西城区" },
              ],
            },
          ],
        },
        {
          code: "12",
          name: "天津市",
          children: [
            {
              code: "1201",
              name: "市辖区",
              children: [
                { code: "120101", name: "和平区" },
                { code: "120102", name: "河东区" },
                { code: "120103", name: "河西区" },
              ],
            },
          ],
        },
      ];

      const institutionsData = [
        { code: "120101", id: "12010101", name: "Placeholder for name 1" },
        { code: "120101", id: "12010102", name: "Placeholder for name 2" },
        { code: "120102", id: "12010201", name: "Placeholder for name 3" },
        { code: "120102", id: "12010202", name: "Placeholder for name 4" },
        { code: "120103", id: "12010301", name: "Placeholder for name 5" },
        { code: "120103", id: "12010302", name: "Placeholder for name 6" },
        { code: "110101", id: "11010101", name: "Placeholder for name 7" },
        { code: "110101", id: "11010102", name: "Placeholder for name 8" },
      ];

      // 加载地市数据到选择器
      function loadCities() {
        const citySelector = document.getElementById("citySelector");
        citiesData.forEach((city) => {
          let option = document.createElement("option");
          option.value = city.code;
          option.textContent = city.name;
          citySelector.appendChild(option);
        });
      }

      // 当用户选择一个地市时,显示该地市的机构信息
      function displayInstitutions(cityCode) {
        const institutionInfoDiv = document.getElementById("institutionInfo");
        institutionInfoDiv.innerHTML = ""; // 清空当前显示的机构信息

        // 查找与选择的地市代码匹配的机构信息
        const filteredInstitutions = institutionsData.filter((inst) =>
          inst.code.startsWith(cityCode)
        );
        if (filteredInstitutions.length === 0) {
          institutionInfoDiv.innerHTML = "该地市没有对应的机构信息。";
        } else {
          const list = document.createElement("ul");
          filteredInstitutions.forEach((inst) => {
            const item = document.createElement("li");
            item.textContent = `${inst.name} (ID: ${inst.id})`;
            list.appendChild(item);
          });
          institutionInfoDiv.appendChild(list);
        }
      }

      // 给地市选择器绑定事件
      function setupCitySelector() {
        const citySelector = document.getElementById("citySelector");
        citySelector.addEventListener("change", function () {
          displayInstitutions(this.value);
        });
      }

      // 初始化页面
      function init() {
        loadCities();
        setupCitySelector();
      }

      init();
    </script>
  </body>
</html>

vue 代码

vue
<template>
  <div>
    <select
      id="citySelector"
      :class="$style.citySelector"
      @change="displayInstitutions($event.target.value)"
    >
      <option v-for="city in citiesData" :key="city.code" :value="city.code">
        {{ city.name }}
      </option>
    </select>

    <div id="institutionInfo">
      <ul v-if="filteredInstitutions.length">
        <li v-for="inst in filteredInstitutions" :key="inst.id">
          {{ inst.name }} (ID: {{ inst.id }})
        </li>
      </ul>
      <div v-else>该地市没有对应的机构信息。</div>
    </div>
  </div>
</template>

<script>
export default {
  name: "CitySelector",
  data() {
    const citiesData = [
      {
        code: "11",
        name: "北京市",
        children: [
          {
            code: "1101",
            name: "市辖区",
            children: [
              { code: "110101", name: "东城区" },
              { code: "110102", name: "西城区" },
            ],
          },
        ],
      },
      {
        code: "12",
        name: "天津市",
        children: [
          {
            code: "1201",
            name: "市辖区",
            children: [
              { code: "120101", name: "和平区" },
              { code: "120102", name: "河东区" },
              { code: "120103", name: "河西区" },
            ],
          },
        ],
      },
    ];

    const institutionsData = [
      { code: "120101", id: "12010101", name: "Placeholder for name 1" },
      { code: "120101", id: "12010102", name: "Placeholder for name 2" },
      { code: "120102", id: "12010201", name: "Placeholder for name 3" },
      { code: "120102", id: "12010202", name: "Placeholder for name 4" },
      { code: "120103", id: "12010301", name: "Placeholder for name 5" },
      { code: "120103", id: "12010302", name: "Placeholder for name 6" },
      { code: "110101", id: "11010101", name: "Placeholder for name 7" },
      { code: "110101", id: "11010102", name: "Placeholder for name 8" },
    ];
    return {
      citiesData: [
        ...citiesData,
        // 这里是您的 citiesData 数组
      ],
      institutionsData: [
        ...institutionsData,
        // 这里是您的 institutionsData 数组
      ],
      filteredInstitutions: [],
    };
  },
  methods: {
    displayInstitutions(cityCode) {
      this.filteredInstitutions = this.institutionsData.filter((inst) =>
        inst.code.startsWith(cityCode)
      );
    },
  },
};
</script>
<style module>
.citySelector {
  appearance: auto;
  -webkit-appearance: auto;
  border: 1px solid #000;
  border-radius: 2px;
}
</style>