day04 vue学习

将登录系统和 To-Do List 系统结合在一起,我们可以创建一个简单的 Vue 应用,该应用具备用户登录功能,并在用户登录后展示其个人 To-Do List。用户可以添加、删除、标记任务完成状态等。我们可以使用 Pinia 来管理用户登录状态和 To-Do 列表数据。

这个应用整体分为几大模块:

  • 主文件 (App.vuemain.js)
  • 路由管理 (router/index.js)
  • 状态管理 (stores/userStore.js)
  • 页面组件 (Login.vueTodoList.vue)

1. 设置项目和安装 Pinia

在有管理员权限下的终端创建项目并测试

npm create vite@latest day4 -- -- template vue

npm install

npm install element-plus

npm run dev

在项目目录里运行下面的命令安装 Pinia和Vue Router:

npm install pinia
npm install pinia vue-router

2. 文件结构

假设项目结构如下:

src
├── App.vue
├── main.js
├── router
│   └── index.js         // 配置 Vue Router
├── stores
│   └── userStore.js     // Pinia 状态管理
└── views
    ├── Login.vue        // 登录界面
    └── TodoList.vue     // To-Do List 界面

3. 主文件 main.jsApp.vue

main.js:应用启动的核心

main.js 文件是应用的入口,负责启动整个 Vue 应用并将其挂载到页面上。

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
import router from './router';

// 创建应用实例,并配置应用的核心插件
const app = createApp(App);
app.use(createPinia());  // 安装 Pinia,用于管理全局状态
app.use(router);         // 安装路由,用于页面跳转

app.mount('#app');       // 将应用挂载到页面上
  • createApp(App):创建一个 Vue 应用实例,准备将应用展示在网页上。
  • app.use(createPinia()):安装 Pinia,用于全局管理数据。
  • app.use(router):安装路由,用于页面跳转。
  • app.mount('#app'):把应用挂载到页面上,通过 HTML 中的 id="app" 来渲染整个应用。

App.vue:页面的主要框架

App.vue 是应用的根组件,它负责展示页面内容。

<!-- src/App.vue -->
<template>
  <div id="app">
    <router-view />  <!-- 在此处展示不同页面内容 -->
  </div>
</template>

<script setup>
// 不需要添加任何逻辑,因为这里只是一个展示页面内容的容器
</script>

<style>
/* 添加基础样式 */
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

h1, h2 {
  font-weight: normal;
  color: #4caf50;
}
</style>

<router-view />:这是路由的占位符。根据用户访问的网址,Vue Router 会决定显示 Login.vue 还是 TodoList.vue,这让应用可以在不同页面之间切换。

4.路由管理 router/index.js

router/index.js 用于设置页面之间的导航。比如用户在登录页面登录后,可以跳转到待办事项页面。

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Login from '../views/Login.vue';
import TodoList from '../views/TodoList.vue';

const routes = [
  { path: '/', name: 'Login', component: Login },
  { path: '/todolist', name: 'TodoList', component: TodoList },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;
  • routes:定义了应用中的路径与组件的对应关系。/ 表示首页路径,加载 Login.vue/todolist 加载 TodoList.vue
  • createRouter:创建一个路由实例,用于管理页面间的导航。
  • export default router:将路由实例导出,供 main.js 引入和使用。

问题:

const router = createRouter({
  history: createWebHistory(),
  routes,
});

这个是什么用法,.createWebHistory是什么?

createRouter 是 Vue Router 4.x 中的一个函数,用于创建路由实例,用来管理 Vue 应用中的页面导航。而 createWebHistory 是一种路由模式,决定了应用的 URL 是如何被管理的。

解释 createRoutercreateWebHistory

  1. createRouter:这是 Vue Router 提供的一个方法,用于创建路由器实例。创建的路由器实例能帮助应用在不同页面间导航,比如在登录页面和待办事项页面之间切换。

  2. createWebHistory:这是 Vue Router 提供的三种路由模式之一,它决定了 URL 的管理方式。createWebHistory 使用浏览器内置的 History API,URL 变得更“干净”、更现代,不包含 # 符号。

    • 示例 URL(Web History):https://example.com/todolist
    • 与其他模式对比
      • createWebHashHistory:URL 中会带有 # 符号(哈希模式),像 https://example.com/#/todolist。这种方式适用于无需后端支持的单页面应用。
      • createMemoryHistory:所有导航都在内存中进行,常用于服务器渲染或一些测试环境。

5.状态管理 stores/userStore.js

userStore.js 负责管理登录状态和用户的待办事项。它通过 Pinia 提供了统一的数据存储。

// src/stores/userStore.js
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    isLoggedIn: false,
    todos: [],
  }),
  actions: {
    login(userName) {
      this.name = userName;
      this.isLoggedIn = true;
    },
    logout() {
      this.name = '';
      this.isLoggedIn = false;
    },
    addTodo(task) {
      this.todos.push({ task, done: false });
    },
    toggleTodo(index) {
      this.todos[index].done = !this.todos[index].done;
    },
    deleteTodo(index) {
      this.todos.splice(index, 1);
    },
  },
});
  • state:用来存储用户状态的数据。
    • name:记录用户名。
    • isLoggedIn:记录用户是否登录。
    • todos:一个待办事项数组,每个事项包含 task(任务名称)和 done(是否完成)。
  • actions:定义了更新状态的方法。
    • login:接受用户名参数,将用户登录。
    • logout:将用户登出,清空用户名和登录状态。
    • addTodo:添加新的待办事项。
    • toggleTodo:切换任务的完成状态。
    • deleteTodo:删除待办事项。

问题:

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    isLoggedIn: false,
    todos: [],
  }),
  actions: {
    login(userName) {
      this.name = userName;
      this.isLoggedIn = true;
    },
    logout() {
      this.name = '';
      this.isLoggedIn = false;
    },
    addTodo(task) {
      this.todos.push({ task, done: false });
    },
    toggleTodo(index) {
      this.todos[index].done = !this.todos[index].done;
    },
    deleteTodo(index) {
      this.todos.splice(index, 1);
    },
  },
});

这个我不理解,我几乎没学过java,但我学过c和python,你帮我类比一下,解释一下每一行的作用,帮助我触类旁通

逐行解释和类比

  1. export const useUserStore = defineStore('user', {...});

    • 作用:创建并导出一个“数据仓库”函数,用于管理用户的登录信息和待办事项。
    • 类比
      • Python:类似于定义一个类,比如 class UserStore
      • C语言:可以类比成创建一个带函数指针的结构体,用于集中管理信息和行为。
  2. state: () => ({...})

    • 作用state 是一个函数,返回一个对象用于存储数据。这里的数据是 nameisLoggedIntodos
    • 类比
      • Pythonstate 相当于类中的实例属性。
      • C语言:可以理解为结构体的成员变量,用于存储用户状态信息。
  3. name: ''

    • 作用:用户的名字,初始为空。
    • 类比
      • Python/C:相当于一个字符串变量 name = ""
  4. isLoggedIn: false

    • 作用:用户是否已登录,初始值为 false
    • 类比
      • Python/C:一个布尔变量 isLoggedIn = False;isLoggedIn = false;
  5. todos: []

    • 作用:待办事项列表,初始为空数组。
    • 类比
      • Pythontodos = [],即一个空列表。
      • C语言:可以想象成动态分配的指针数组。

actions 部分

actions 包含对数据执行操作的方法,类似于类中的方法或 C 中的函数指针。它们可以直接修改 state 中的数据。

login(userName)

  • 作用:将 name 更新为 userName,并设置 isLoggedIntrue
  • 类比
    • Pythondef login(self, userName): self.name = userName; self.isLoggedIn = True
    • C语言:类似于更新结构体成员的函数 void login(UserStore* store, char* userName)

logout()

  • 作用:清空 name 并设置 isLoggedInfalse
  • 类比
    • Pythondef logout(self): self.name = ""; self.isLoggedIn = False
    • C语言:更新 UserStore 成员变量的值。

addTodo(task)

  • 作用:添加一个新的待办事项,task 是待办内容,默认为未完成。
  • 类比
    • Pythonself.todos.append({"task": task, "done": False})
    • C语言:可以想象成向结构体的指针数组添加新的字符串。

toggleTodo(index)

  • 作用:切换指定任务的完成状态。
  • 类比
    • Pythonself.todos[index]["done"] = not self.todos[index]["done"]
    • C语言:可以想象成通过索引修改数组成员的布尔值。

deleteTodo(index)

  • 作用:根据索引删除待办事项。
  • 类比
    • Pythonself.todos.pop(index)
    • C语言:手动管理数组的内存,移除指定项。

6.页面组件 Login.vueTodoList.vue

Login.vue:用户登录界面

登录页面让用户输入用户名并点击“登录”按钮,完成登录后跳转到 TodoList.vue

<!-- src/views/Login.vue -->
<template>
  <div class="login-container">
    <h1>Welcome to the App</h1>
    <input v-model="userName" placeholder="Enter your name" />
    <button @click="handleLogin">Login</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useUserStore } from '../stores/userStore';
import { useRouter } from 'vue-router';

const userStore = useUserStore();
const router = useRouter();
const userName = ref('');

const handleLogin = () => {
  if (userName.value.trim()) {
    userStore.login(userName.value.trim());
    router.push({ name: 'TodoList' });
  }
};
</script>

<style scoped>
/* 样式:布局居中、按钮美化 */
</style>
  • v-model="userName":使用 v-model 双向绑定让输入框和 userName 变量同步。
  • handleLogin:当点击“登录”按钮时,检查 userName 是否为空,如果有值,则调用 userStore.login(userName) 将用户登录,并跳转到 TodoList 页面。

TodoList.vue:待办事项界面

TodoList.vue 页面展示用户的待办事项列表,并支持添加、删除和标记完成。

<template>
  <div class="todo-container">
    <h2>{{ userStore.name }}'s To-Do List</h2>
    <button @click="handleLogout" class="logout-btn">Logout</button>
    
    <div class="input-container">
      <input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
      <button @click="addTask">Add</button>
    </div>
    
    <ul class="ul-container">
      <li v-for="(todo, index) in userStore.todos" :key="index" class="todo-item">
        <input type="checkbox" v-model="todo.done" class="checkboxs"/>
        <span :class="{ done: todo.done }">{{ todo.task }}</span>
        <button @click="removeTask(index)" class="delete-btn">Delete</button>
      </li>
    </ul>
    
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useUserStore } from '../stores/userStore';
import { useRouter } from 'vue-router';

const userStore = useUserStore();
const router = useRouter();
const newTask = ref('');

const addTask = () => {
  if (newTask.value.trim()) {
    userStore.addTodo(newTask.value.trim());
    newTask.value = '';
  }
};

const removeTask = (index) => {
  userStore.deleteTodo(index);
};

const handleLogout = () => {
  userStore.logout();
  router.push({ name: 'Login' });
};
</script>

<style scoped>
.todo-container {
  width: 80%;
  max-width: 500px;
  margin: auto;
  text-align: center;
  margin-top: 50px;
}

.input-container {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}

input {
  padding: 10px;
  width: 80%;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-right: 5px;
}

button {
  padding: 10px;
  background-color: #4caf50;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.logout-btn {
  background-color: #f44336;
  margin-bottom: 20px;
}

.ul-container {
  list-style: none;
  padding: 0;
  margin: 0 auto;         /* 将列表容器居中 */
  text-align: center;     /* 居中对齐文本 */
  width: 80%;             /* 设置宽度,例如 80% */
  max-width: 600px;       /* 最大宽度 */
  height: 400px;          /* 设置高度 */
  overflow-y: auto;       /* 允许垂直滚动(如果高度超出) */
  border: 1px solid #c4f1ec; /* 可选:添加边框,方便查看宽高 */
  background-color: #cdf3d4; /* 可选:背景颜色 */
}

.checkboxs{
  width: 80%;             /* 设置宽度,例如 80% */
  max-width: 20px;       /* 最大宽度 */

}



.todo-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 10px 0;
}

.todo-item input[type="checkbox"] {
  margin-right: 10px;
}

.done {
  text-decoration: line-through;
}

.delete-btn {
  background-color: #f44336;
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
}
</style>
  • addTask:用来添加新任务。
  • removeTask:用来删除特定的任务。
  • handleLogout:登出后返回登录页面。

问题:

    <div class="input-container">
      <input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
      <button @click="addTask">Add</button>
    </div>

keyup.enter是什么?

  • @keyup.enter="addTask"
    • @keyup 监听 keyup 事件,即当用户松开一个键时触发。
    • .enter 是修饰符,仅在用户松开 Enter 键时触发事件。
    • addTask 是调用的方法名,在用户按下 Enter 键后,调用 addTask 方法。

作用

用户在输入框中输入任务内容时,按下 Enter 键 会自动调用 addTask 方法,添加任务到待办列表,而无需点击“Add”按钮。

    <ul>
      <li v-for="(todo, index) in userStore.todos" :key="index" class="todo-item">
        <input type="checkbox" v-model="todo.done" />
        <span :class="{ done: todo.done }">{{ todo.task }}</span>
        <button @click="removeTask(index)" class="delete-btn">Delete</button>
      </li>
    </ul>

这里面每一行是什么意思?

<ul> ... </ul>

  • 作用:创建一个无序列表,用于显示多个待办事项(<ul> 是 HTML 的标签表示无序列表)。
  • 渲染内容:列表项将放在 <ul> 标签内显示。

<li v-for="(todo, index) in userStore.todos" :key="index" class="todo-item">

  • v-for="(todo, index) in userStore.todos":Vue 指令,用于遍历 userStore.todos 数组。todo 是当前循环的待办项,每个待办项有 taskdone 属性,index 是当前项目的索引。
  • :key="index":每个列表项的唯一标识,有助于 Vue 更高效地跟踪和更新每一项。
  • class="todo-item":添加一个 CSS 类名 todo-item,方便样式控制。

<input type="checkbox" v-model="todo.done" />

  • 作用:复选框,用于显示和更新每个待办事项的完成状态。
  • v-model="todo.done":双向绑定,将待办事项的 done 状态绑定到复选框。勾选或取消勾选复选框时,todo.done 的值会自动更新。

<span :class="{ done: todo.done }">{{ todo.task }}</span>

  • {{ todo.task }}:显示待办事项的任务内容(文本)。
  • :class="{ done: todo.done }":动态绑定类名。若 todo.donetrue,则 span 元素将会有 done 类,可以用 CSS 设置完成任务的样式,比如显示删除线。

<button @click="removeTask(index)" class="delete-btn">Delete</button>

  • 作用:删除按钮,每次点击删除当前待办项。
  • @click="removeTask(index)":Vue 指令,绑定 click 事件,点击时调用 removeTask 方法并传入 index,从 userStore.todos 中移除指定的待办项。
  • class="delete-btn":CSS 类名 delete-btn,用于样式设置,比如调整按钮的外观。

7.项目效果

  • 登录页面:用户可以在此输入用户名并点击登录。登录后会自动跳转到 To-Do List 页面。
  • To-Do List 页面:用户可以添加任务、标记完成状态、删除任务,并包含“退出登录”按钮。点击“退出登录”会返回到登录页面。

第五天任务:学习 Vue 3 中的路由管理(Vue Router),实现不同页面间的切换与用户权限管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911298.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HFSS 3D Layout中Design setting各个选项的解释

从HFSS 3D LAYOUT菜单中&#xff0c;选择Design Settings打开窗口&#xff0c;会有六个选项&#xff1a;DC Extrapolation, Nexxim Options, Export S Parameters, Lossy Dielectrics, HFSS Meshing Method, and HFSS Adaptive Mesh. DC Extrapolation 直流外推 直流外推分为标…

【板栗糖GIS】——如果安装的vscode版本落后了,如何无障碍更新

【板栗糖GIS】——如果安装的vscode版本落后了&#xff0c;如何无障碍更新 今天想安装新扩展插件时发现vscode版本有点旧&#xff0c;于是在不影响插件的情况下更新。 打开vscode软件&#xff0c;点击设置检查更新&#xff0c;如果有需要更新的直接安装到原目录&#xff0c;如…

数字化转型必看!华为数字化最全合集(192页PDF限免下载 )

今天给大家整理了6份关于华为数字化的资料&#xff0c;共计192页&#xff0c;干货满满&#xff01; 资料已经全部打包&#xff0c;划到文末添加大师兄即可免费下载&#x1f447;&#x1f447;&#x1f447; 一、华为实施数字化转型方法论与实践的业务解读 这份报告是华为实施数…

【数据集】【YOLO】【目标检测】道路结冰数据集 1527 张,YOLO目标检测实战训练教程!

数据集介绍 【数据集】道路结冰数据集 1527 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。数据集中包含2种分类&#xff1a;“clear_road, ice_road”。数据集来自国内外图片网站和视频截图&#xff0c;部分数据经过数据增强处理。检测范围监控视角检测、无人机视…

创建线程时传递参数给线程

在C中&#xff0c;可以使用 std::thread 来创建和管理线程&#xff0c;同时可以通过几种方式将参数传递给线程函数。这些方法包括使用值传递、引用传递和指针传递。下面将对这些方法进行详细讲解并给出相应的代码示例。 1. 值传递参数 当你创建线程并希望传递参数时&#xff…

集智书童 | DuoDiff: 提升浅层 Transformer 性能的扩散模型, 双 Backbone 件扩散模型在图像处理中的应用 !

本文来源公众号“集智书童”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;DuoDiff: 提升浅层 Transformer 性能的扩散模型&#xff0c; 双 Backbone 件扩散模型在图像处理中的应用 &#xff01; 扩散模型在图像生成方面取得了前所…

linux perf 环境部署和基本测试(基于Ubuntu20.04)

1,linux 安装perf sudo apt-ge install linux-tools-common sudo apt-get install linux-tools-$(uname -r) linux-tools-generic -y 2 补充安装 sudo apt-get install python3-q-text-as-data 3&#xff0c;perf常用命令 larkubuntu:~$ perf usage: perf [--version] [--hel…

PHP露营地管理平台小程序系统源码

⛺️【露营新风尚】露营地管理平台系统全攻略⛺️ &#x1f3d5;️一、露营热潮下的管理难题&#xff1a;如何高效运营露营地&#xff1f;&#x1f914; 随着露营文化的兴起&#xff0c;越来越多的人选择在大自然中享受宁静与自由。然而&#xff0c;露营地的管理却面临着诸多…

信息安全工程师(83)Windows操作系统安全分析与防护

一、Windows操作系统安全分析 系统漏洞&#xff1a; Windows操作系统由于其复杂性和广泛使用&#xff0c;可能存在一些已知或未知的漏洞。这些漏洞可能会被黑客利用&#xff0c;进行恶意攻击。微软会定期发布系统更新和补丁&#xff0c;以修复这些漏洞&#xff0c;提高系统的安…

软件测试—功能测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、测试项目启动与研读需求文档 &#xff08;一&#xff09; 组建测试团队 1、测试团队中的角色 2、测试团队的基本责任 尽早地发现软件程序、系统或产…

【1个月速成Java】基于Android平台开发个人记账app学习日记——第7天,申请阿里云SMS短信服务SDK

系列专栏链接如下&#xff0c;方便跟进&#xff1a; https://blog.csdn.net/weixin_62588253/category_12821860.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12821860&sharereferPC&sharesourceweixin_62588253&sharefromfrom_link 同时篇幅…

安装和运行开发微信小程序

下载HBuilder uniapp官网 uni-app官网 微信开发者工具 安装 微信小程序 微信小程序 官网 微信小程序 配置 运行 注意&#xff1a;运行前需要开启服务端口 如果运行看不到效果&#xff0c;设置下基础库选别的版本 配置

如何用pycharm连接sagemath?

#世纪难题在我逃避刷CTF的这两天解决了# 1. 在本地linux上部署最新版的sagemath 推荐WSLdocker直接pull sagemath 2. pycharm中创建jupyter脚本&#xff0c;远程连接jupyter服务器 3. 运行cell并配置kernel 缺点&#xff1a;pycharm用自带的python编译器预处理代码&#xff0…

JNPF V5.1版本震撼上线,更多功能等你解锁!

亲爱的用户们&#xff1a; 随着季节的更迭&#xff0c;引迈也在不断进步和创新。经过数月的精心打磨和无数次的测试&#xff0c;我们非常兴奋地宣布&#xff0c;JNPF快速开发平台迎来了激动人心的V5.1版本更新&#xff01;这次更新不仅带来了全新的功能和改进&#xff0c;还进…

Dependency: androidx.webkit:webkit:1.11.0-alpha02. 问题

android studio 打包后出现这个问题 1.步骤更新topOn sdk 添加 //Admob api “com.anythink.sdk:adapter-admob:6.4.18” api “com.google.android.gms:play-services-ads:23.4.0” api "com.google.android.gms:play-services-ads:23.4.0"sdk 中会出现打包编译报错…

express 使用JWT认证

1、JWT的理解 JWT 的组成部分: 分别是 Header(头部)、Payload(有效荷载)、Signature(签名) 三者之间使用英文的"."分隔, Pyload 部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串 Header 和 Signature 是 安全性相关的部分&#xff0c;只是为了保证 Tok…

【数据结构】LRUCache和跳表{简单讲解+模拟实现}

文章目录 LRUCacheSkipList LRUCache LRU是Least Recently Used的缩写&#xff0c;即最近最少使用&#xff0c;是一种常用的页面置换算法&#xff0c;选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段&#xff0c;用来记录一个页面自上次被访问以来所经历的…

第一个纯血鸿蒙应用(Napi开发-ArtTS调用C/C++)

1.行业背景 纯血鸿蒙&#xff0c;即鸿蒙Next版已于2014年1月正式发版&#xff0c;鸿蒙生态设备数量已经突破10亿台&#xff0c;已经有超过15000个应用和元服务上架。鸿蒙生态不只是移动设备这么简单&#xff0c;他打造的是一个18n的全场景战略&#xff0c;真正做到了“万物互联…

【从零开始的LeetCode-算法】3255. 长度为 K 的子数组的能量值 II

给你一个长度为 n 的整数数组 nums 和一个正整数 k 。 一个数组的 能量值 定义为&#xff1a; 如果 所有 元素都是依次 连续 且 上升 的&#xff0c;那么能量值为 最大 的元素。否则为 -1 。 你需要求出 nums 中所有长度为 k 的 子数组 的能量值。 请你返回一个长度为 n - …

【Linux】进程创建/等待/替换相关知识详细梳理

1. 进程创建 1.1 fork函数 概念&#xff1a;fork函数为系统调用接口&#xff0c;用于从已存在的进程中&#xff0c;创建一个新的进程。新进程为子进程&#xff0c;原来的进程为父进程。 用法&#xff1a; #include <unistd.h> pid_t fork(void); // 返回值&…