Skip to content

项目相关记录

TS语法

!! 语法

ts
!! ts中的语法 把其他类型强制转为 布尔值
示例:
const haslogin = !!useinfo.username

? 语法

ts
? ts中的语法,当passwordRef.value存在时,执行passwordRef.value.focus(),不存在时不执行
passwordRef.value?.focus()

定义TS类型

shell
# 从小往大写

VITE构建工具

ts
VITE配置的环境变量必须以 VITE_ 开头

vue 构建脚手架的工具是 cli,本次项目使用的构建脚手架的工具是 vite

vue3时,vite运行的速度很快
ts
然后按照提示操作即可!

你还可以通过附加的命令行选项直接指定项目名称和你想要使用的模板。例如,要构建一个 Vite + Vue 项目,运行:

# npm 6.x
npm init vite@latest my-vue-app --template vue

# npm 7+, 需要额外的双横线:
npm init vite@latest my-vue-app -- --template vue

# yarn
yarn create vite my-vue-app --template vue

# pnpm
pnpm create vite my-vue-app -- --template vue

配置代理跨域

ts
需要到 vite的官网 查看配置 服务器选项

Vscode设置

Element插件

ts
在vs中安装 
Element UI Snippets 作用:快速添加element plus组件

代码片段

ts
点击 vscode 左下角的齿轮设置按钮,点击用户代码片段
输入 vue,选择 vue.json 文件
使用时输入 vue3 即可快速生成 vue3 模板
ts
{
	"Print to console": {
        "prefix": "vue3",  // 模板的名称
        "body": [          // 模板的结构(骨架)
            "<template>",
			" $1",
            "</template>",
            "",
            "<script setup lang='ts'>",
			" $1",
            "</script>",
            "",
            "<style scoped lang=\"less\">",
            "</style>"
        ],
        "description": "Log output to console",
    }
}

Vu3模版插件

shell
安装插件:Vue3 Snippets
使用命令:v3ts

时间插件

moments插件

moments和dayjs是一样的

ts
npm install moments
引入:import moments from 'moments'
取值:(当前时间)
moments.format("") // 时间格式

dayjs插件

https://dayjs.fenxianglu.cn/category/parse.html#utc

ts
安装:
	npm install dayjs
引入:(在组件中)
	import dayjs from 'dayjs'
取值(当前时间)
    dayjs() // 返回一个对象
    dayjs().format('')
    // 默认是当地时间
    dayjs().format("YYYY-MM-DD") //2019-03-06

Element-plus

升级element-plus

shell
npm uninstall element-plus
npm install element-plus

根据版本改API,看版本

自定义标签-插槽

默认el-table-column中的数据使用div标签进行渲染,当需要使用button,img,a标签进行渲染时,就需要插槽来实现

vue
<!-- element中默认已经写好了 slot的出口 -->
<el-table-column label prop >
    <!-- 使用作用域插槽 #是v-slot的简写 解构出子组件传过来的 row:行对象 $index:索引值-->
	<template #="{row,$index}">
    	<img :src='row.url' alt=''  />
    </template>
</el-table-column>

form组件

使用案例:

vue
<template>
<el-form-item label="SPU销售属性">
    <el-select v-model="SaleAttrNameNoHasSelectStr" style="width: 240px">
        <el-option
            v-for="(item, index) in SaleAttrNameNoHasList"
            :key="item.id"
            :label="item.name"
            :value="`${item.id}:${item.name}`">
        </el-option>
    </el-select>
    <el-button type="primary" :icon="Plus" style="margin-left: 10px" 
        @click="addSaleAttr"
        :disabled="SaleAttrNameNoHasSelectStr ? false : true">
        添加销售属性
    </el-button>
</el-form-item>
</template>

<script>

</script>

table组件

使用案例

vue
<template>
    <el-dialog title="SKU列表" v-model="showDailog">
        <el-table border :data="skuList" v-loading="tableLoading">
            <el-table-column label="SKU名称" prop="skuName"></el-table-column>
            <el-table-column label="SKU价格" prop="price"></el-table-column>
            <el-table-column label="SKU重量" prop="weight"></el-table-column>
            <el-table-column label="SKU图片">
                <template #="{ row, $index }">
                    <img :src="row.skuDefaultImg" alt="" style="width: 100px; height: 100px" />
                </template>
            </el-table-column>
        </el-table>
    </el-dialog>
</template>

<script>

</script>

layout布局

通过基础的 24 分栏,迅速简便地创建布局

vue
<template>
  <el-row :gutter="20">
    <el-col :span="16"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="8"><div class="grid-content ep-bg-purple" /></el-col>
  </el-row>
  <el-row :gutter="20">
    <el-col :span="8"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="8"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="4"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="4"><div class="grid-content ep-bg-purple" /></el-col>
  </el-row>
  <el-row :gutter="20">
    <el-col :span="4"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="16"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="4"><div class="grid-content ep-bg-purple" /></el-col>
  </el-row>
</template>

<script>

</script>
<style>
.el-row {
  margin-bottom: 20px;
}
.el-row:last-child {
  margin-bottom: 0;
}
.el-col {
  border-radius: 4px;
}

.grid-content {
  border-radius: 4px;
  min-height: 36px;
}
</style>

Pagination分页

vue
<template>

</template>

<script>

</script>

select组件

vue
<template>
<el-select v-model="SaleAttrNameNoHasSelectStr" style="width: 240px">
    <el-option
        v-for="(item, index) in SaleAttrNameNoHasList"
        :key="item.id"
        :label="item.name"
        :value="`${item.id}:${item.name}`">
    </el-option>
</el-select>
</template>

<script>

</script>

button组件

vue
<template>
 <div class="mb-4">
    <el-button>Default</el-button>
    <el-button type="primary">Primary</el-button>
    <el-button type="success">Success</el-button>
    <el-button type="info">Info</el-button>
    <el-button type="warning">Warning</el-button>
    <el-button type="danger">Danger</el-button>
  </div>
</template>

<script>

</script>

深度选择器

scope属性

ts
# 父组件style上设置 scoped 属性
	作用:局部样式,父组件中的样式影响父组件中的样式和子组件根节点的样式
    原理:在当前组件dom所有节点上以及子组件根节点上添加相同的自定义属性(date-v-哈希值),元素的style属性会自动添加[date-v-哈希]属性,然后通过属性选择器来控制不同组件的不同样式。
	注意:父组件设置scoped属性,子组件也设置scoped属性,那么子组件上的根节点,就会有两个自定义属性,分别是父组件和子组件的

如果在父组件影响子组件中的样式

ts
# 第一种方式:可以在父组件上去掉 scope 属性

# 第二种方式:使用深度选择器
	作用:样式穿透

三种用法

ts
# 第一种: 原生CSS用法: >>>选择器
<style scope>
    >>>h1{
        color:red;
    }
</style>
# 第二种: less和css语法用法: /deep/选择器
<style scope>
    /deep/h1{
        color:red;
    }
</style>
# 第三种: 老sass和css语法用法: ::v-deep 选择器
<style scope>
    ::v-deep h1{
        color:red;
    }
</style>
# 第四种: 新sass语法用法: ::v-deep(选择器)
<style scope>
    ::v-deep(h1){
        color:red;
    }
</style>

使用案例

修改按钮颜色
css
::v-deep(.el-button.is-disabled){
    background-color: orange !important;
}
修改分页器
ts
使用 !importent 提升权限
css
::v-deep(.is-active){
    background-color: red !important;
}
修改轮播图
ts
先通过 开发者工具 查看修改元素的 类名
然后通过类名选择器选择到元素,并且结合深度选择器穿透到元素上
vue
# 修改指示器,就是修改button按钮的样式
css
::v-deep .el-carousel__button {
  width: 10px;
  height: 10px;
  background: aqua;
  border-radius: 50%;
}

Tree组件

vue
# 树形控件


<template>
  <el-tree
    style="max-width: 600px"
    :data="data"
    :props="defaultProps"
    @node-click="handleNodeClick"
  />
</template>

<script lang="ts" setup>
interface Tree {
  label: string
  children?: Tree[]
}

const handleNodeClick = (data: Tree) => {
  console.log(data)
}

const data: Tree[] = [
  {
    label: 'Level one 1',
    children: [
      {
        label: 'Level two 1-1',
        children: [
          {
            label: 'Level three 1-1-1',
          },
        ],
      },
    ],
  },
  {
    label: 'Level one 2',
    children: [
      {
        label: 'Level two 2-1',
        children: [
          {
            label: 'Level three 2-1-1',
          },
        ],
      },
      {
        label: 'Level two 2-2',
        children: [
          {
            label: 'Level three 2-2-1',
          },
        ],
      },
    ],
  },
  {
    label: 'Level one 3',
    children: [
      {
        label: 'Level two 3-1',
        children: [
          {
            label: 'Level three 3-1-1',
          },
        ],
      },
      {
        label: 'Level two 3-2',
        children: [
          {
            label: 'Level three 3-2-1',
          },
        ],
      },
    ],
  },
]

const defaultProps = {
  // 设置children的别名,指向数据中的真实属性名
  children: 'children',
  label: 'label',
}
</script>

table组件

vue
<template>
  <div>
    <el-table
      :data="tableData"
      style="width: 100%; margin-bottom: 20px"
      row-key="id"
      border
      default-expand-all
    >
      <el-table-column prop="date" label="Date" sortable />
      <el-table-column prop="name" label="Name" sortable />
      <el-table-column prop="address" label="Address" sortable />
    </el-table>

    <el-table
      :data="tableData1"
      style="width: 100%"
      row-key="id"
      border
      lazy
      :load="load"
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="date" label="Date" />
      <el-table-column prop="name" label="Name" />
      <el-table-column prop="address" label="Address" />
    </el-table>
  </div>
</template>
<script lang="ts" setup>
interface User {
  id: number
  date: string
  name: string
  address: string
  hasChildren?: boolean
  children?: User[]
}

const load = (
  row: User,
  treeNode: unknown,
  resolve: (data: User[]) => void
) => {
  setTimeout(() => {
    resolve([
      {
        id: 31,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles',
      },
      {
        id: 32,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles',
      },
    ])
  }, 1000)
}

const tableData: User[] = [
  {
    id: 1,
    date: '2016-05-02',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    id: 2,
    date: '2016-05-04',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    id: 3,
    date: '2016-05-01',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
    children: [
      {
        id: 31,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles',
      },
      {
        id: 32,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles',
      },
    ],
  },
  {
    id: 4,
    date: '2016-05-03',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
]

const tableData1: User[] = [
  {
    id: 1,
    date: '2016-05-02',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    id: 2,
    date: '2016-05-04',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    id: 3,
    date: '2016-05-01',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    id: 4,
    date: '2016-05-03',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
  },
]
</script>

Vue使用Echarts

入门使用

ts
# 安装插件
npm install echarts

# 在组件中导包
import * as echarts from 'echarts'
import {ref,onMounted} from "vue"
import * as echarts from 'echarts';
import {dataJson} from "./data/data.ts"

// 注册
echarts.registerMap('china', {geoJSON: dataJson});

# 准备一个 具有高度 的容器
<div class = 'bar' ref="bar" style="height:600px;width:400px"></div>


# 初始化echars实例( onMounted 时挂载)
onMounted(()=>{
    const mycharts = echarts.init(bar.value)
    // 创建图形
    mycharts.setOption(option)
    // 配置
    var option = {
        geo: [
            {
                map: 'china', // 配置地图名
                label:{ // 文本标签
                    show:true,
                    color:aqua
                },
                itemStyle:{ // 区域设置
                	// 颜色
            	},
            	emphasis:{ // 高亮设置
            		label:{ // 高亮标签设置
                        fontSize:40 // 高亮字体大小
                        
                    },
                    itemStyle:{ // 高亮元素设置
                        areaColor:"bluesky" // 高亮元素区域颜色设置
                    }
            	},
                roam:true // 开启地图缩放
            },
        ]
        series:[
            {	// 飞机航线
                type:"lines", 
                data:[
        			{
                        coords: [
                            [120, 66],  // 起点
                            [122, 67],   // 终点
                            ...         // 如果 polyline 为 true 还可以设置更多的点
                        ],
                        // 统一的样式设置
                        lineStyle: { // 设置线的颜色
                            // 设置颜色
                        },
					},
        		],
          		symbol:"", // 设置线两端的标记 设置线两端的标记点,把标记点设置为 图片
          		effect:{ // 设置线的特效
          			show:true, // 开启动画
          			symbol = 'circle', // 动画两端的标记,可以设置为图片
          			color = 'red', // 设置颜色
          		}
            }
        ]
	}
})

scss语法

scss和less很像

ts
# 不同点
    less中的变量 使用@定义变量
    scss中的变量 使用$定义变量

# 相同的
	可以嵌套

Vue3语法

导出省市区数据方法

ts
function extractProvinceData(data) {
  // 用于存储统计结果
  const provinceMap = {};

  // 遍历记录,提取省份和区域信息
  data.forEach(item => {
    const { snName } = item; // 获取包含省份和区域信息的字段

    if (snName) {
      // 提取省份和区域
      const match = snName.match(/X?(.*省)(.*|.*区)/); // 允许省份前带有"X"
      if (match) {
        const province = match[1];
        const area = match[2];

        // 初始化省份数据
        if (!provinceMap[province]) {
          provinceMap[province] = {
            name: province.replace('省', ''),
            value: 0,
            areas: new Set(), // 使用 Set 去重
          };
        }

        // 更新省份数据
        provinceMap[province].value += 1;
        provinceMap[province].areas.add(area.replace('市', '').replace('区', ''));
      }
    }
  });

  // 转换为目标数组格式
  const result = Object.values(provinceMap).map(province => ({
    name: province.name,
    value: province.value,
    areas: Array.from(province.areas), // 将 Set 转为数组
  }));

  return result;
}

// 示例数据(简化版)
const data = [
  {
    snName: "河南省郑州市金水区大林测试0822"
  },
  {
    snName: "X陕西省西安市莲湖区西安莲湖土门十字西二环庆安招待所综合机房DF21587Bb"
  },
  {
    snName: "湖北省武汉市黄陂区沙口社区11086"
  },
  {
    snName: "山东省济南市槐荫区某地"
  },
  {
    snName: "河南省郑州市新郑市比亚迪三期"
  }
];

console.log(extractProvinceData(data));
ts
正则表达式 /X?(.*省)(.*|.*区)/ 的具体功能:

X?
匹配一个可选的 "X"(即 "X" 可能存在,也可能不存在)。
比如,"X陕西省""陕西省" 都可以匹配。
(.*省):
捕获以 "省" 结尾的子字符串,提取省份名称。
例如 "河南省""陕西省"
(.*|.*区):
捕获以 "市""区" 结尾的子字符串,提取区域名称。
如果是 "郑州市""金水区",都可以匹配。
正则匹配结果:

// 去除 "X",并匹配 "陕西省"
let result = snName.replace(/^X/, '').match(/(陕西省)/);

假设 snName = "X陕西省西安市莲湖区", 使用 .match() 方法会返回以下结果:
javascript
复制代码
["X陕西省西安市", "陕西省", "西安市"]
ts
const snName = "X陕西省西安市莲湖区";

// 去掉 "X" 并匹配省或直辖市和市或区
const match = snName.replace(/^X/, '').match(/(.*|.*市)(.*|.*区)/);

if (match) {
    const firstPart = match[1]; // 省或直辖市部分
    const secondPart = match[2]; // 市或区部分
    console.log(firstPart); // 输出: 陕西省
    console.log(secondPart); // 输出: 西安市莲湖区
} else {
    console.log("没有匹配到结果");
}

Vue类型声明问题

TypeScript 无法识别 .vue 文件的类型时,需要为其添加类型声明文件。

在 src 目录下创建文件 shims-vue.d.ts,内容如下

ts
declare module '*.vue' {
  import { DefineComponent } from 'vue';
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

使用动态import()(vite)

ts
const _import = (file: string) => {
    if (process.env.NODE_ENV === 'development') {
        return import(`../views/${file}.vue`);
    } else if (process.env.NODE_ENV === 'production') {
        return import(`../views/${file}.vue`);
    } else {
        throw new Error('未支持的 NODE_ENV 值');
    }
};
// 动态使用import2
const _import = (file: string) => {
    if (process.env.NODE_ENV === 'development') {
        return () => import(`../views/${file}.vue`);
    } else if (process.env.NODE_ENV === 'production') {
        return () => import(`../views/${file}.vue`);
    } else {
        throw new Error('未支持的 NODE_ENV 值');
    }
}

// 菜单转路由类型
/**
 * 
 * @param menus 菜单列表
 */
export function ConvertToRoute(menus: any) {
    // loadComponent("core/device/list").then(()=>
    //     console.log('已注册')
    // )
    const res: any = []
    menus.forEach((menu: any) => {
        // 解构每个路由的配置项
        const { path, component, name, children, icon, hidden, redirect } = menu

        const route: any = {
            path,
            name,
            meta: {
                hidden: hidden === 0 ? false : true,
                title: name || '', // 设置标题
                icon: icon || '',  // 设置图标
            },
            redirect: redirect ? redirect : ''

        };

        // 动态设置component
        // if (component) {
        //     route.component =
        //         component === 'Layout'
        //             ? () => import('@/layout/index.vue') // 动态引入 Layout 组件
        //             : () => import('@/views/core/device/list.vue'); // 动态引入视图组件
        // }
        if (component) {
            if (component === 'Layout') {
                route.component = () => import('@/layout/index.vue')
            } else {
                route.component = _import(component)
            }
        }

        // 递归处理子路由
        if (children && children.length > 0) {
            route.children = ConvertToRoute(children);
        }

        // 添加到结果数组
        res.push(route);
    });
    // 返回数组
    return res
}

使用动态import()(vue2)

ts
onst _import = require('@/router/_import_' + process.env.NODE_ENV);
tmp.component = _import(tmp.component);

require('@/router/_import_' + process.env.NODE_ENV):根据环境变量动态加载不同的 _import 文件,例如 _import_development.js 或 _import_production.js。

tmp.component = _import(tmp.component):将 tmp.component(字符串形式的组件路径)转换为实际的组件对象。

History部署nginx

ts
在生产环境中,如果你使用了 history 模式,但没有正确配置服务器(如 Nginx)以支持 SPA 路由,则可能导致路径无法匹配。以下是 Nginx 的配置示例:

Nginx 配置(确保 Vue Router 的 history 模式正常工作)
nginx

server {
  listen 80;
  server_name yourdomain.com;

  root /path/to/your/dist;
  index index.html;

  location / {
    try_files $uri /index.html;
  }
}

v-show与v-if的区别

shell
v-show是 显示与隐藏:dom元素没有发生变化
v-if是 新增和删除:dom元素发生了变化

v-show绑定的dom对象不需要使用nextTick,因为v-show是隐藏
v-if需要nextTick获取更新后的dom

生命周期

ts
// 生命周期钩子中不写 异步函数,异步函数单独写出来
onMounted(()=>{
    getTradeMarkList()
})
// 定义异步函数
const getTradeMarkList = async () => {
    await 调用接口
}

组件中设置name属性

在组件中设置name,这样在vue的调试工具中就不会全部显示index组件,而是设置的name,方便找到对应的组件。

vue
<template>

</template>

<script lang="ts">
export default {
    name:"组件名称"
}
</script>

响应式数据

ts
vue2 响应式原理是Object.protopoty,只能监听到某个对象的某个属性
vue3 响应式代理对象reactive对象的底层原理是Proxy对象,动态追加的数据也是响应式,可以监听到整个对象的变化

nextTick()

ts
响应式数据变化,Dom元素不会立即渲染出来,需要使用nextTick()函数获取最新的dom之后,执行

全局组件

ts
vue3 设置全局组件的方法

import Category from 'src/components/Category/index.vue'
// 挂载为全局组件
app.component('Category',Category)

全局指令

ts
// 1 在src/utils/文件夹中 新建一个ts文件 directive.ts 新建自定义指令

//引入用户相关的小仓库
import pinia from '@/stores';
import {useUserInfoStore} from '@/stores/userInfo';

//获取用户小仓库
const  userStore = useUserInfoStore(pinia);
export const has = (app:any)=>{
    // 设置全局指令has 使用directive函数
    // 第一个参数是 指令名字,第二个参数是 自定义指令钩子函数
    app.directive("has",{
        // 当使用了自定义指令的按钮dom元素,挂载完成后执行回调函数
        
        // mounted(element,options)函数的
        // 第一个参数element是 使用了自定义指令的按钮dom元素 
        // 第二个参数options是 自定义指令对象(可以设置修饰符,获取自定义指令的value值)
        mounted(element,options){
			// 通过JS原生dom操作 删除按钮元素
        }
    })
    
    
}

// 2 在入口文件全局自定义指令 
// vue2中注册全局自定义指令 vue.directive()
// vue3中注册全局自定义指令 app.directive()
import {has} from '@/utils/directive.ts'
// 将app注入
has(app)

// 3 在组件中使用
<!-- 添加按钮 -->
<el-button
    type="primary"
    :icon="Plus"
    @click="addSpu"
    v-has="'btn.Spu.add'"
    :disabled="categoryStore.c3Id ? false : true">添加SPU
</el-button>
ts
判断是否渲染按钮思路
	在所有按钮上设置 v-has 属性
    获取用户仓库中的按钮数组数据
    	当按钮仓库中的数组元素中没有 option.value 中的值时,不渲染按钮
		当按钮仓库中的数组元素中存在 option.value 中的值时,渲染按钮

使用案例

定义自定义指令:src/utils/directive.ts

ts
// 在非组件页面下使用小仓库,需要先引入大仓库
import pinia from "@/stores"
import {useUserInfoStore} from "@/stores/userInfo"
// 注入大仓库
const userStore = useUserInfoStore(pinia)
// 暴漏一个函数
//对外暴露一个函数
export const has = (app:any)=>{
    //生成按钮权限全局自定义指令
    //第一个参数:自定义指令的名字
    app.directive("has",{
      //当使用v-has这个全局自定义指令的DOM|组件挂载完毕的时候会立即执行一次
      //element:使用这个指令的DOM元素
      //options:能获取有自定义只有右侧数值  v-has="btn.Trademark.add"
        mounted(element:any,options:any) {
          //判断自定义指令右侧数值在仓库的buttons数组当中是否出现!!!
            if(!userStore.buttons.includes(options.value)){
            //获取当前绑定自定义指令DOM元素父组件,通过removeChild方法将当前元素删除!!!
                element.parentNode.removeChild(element);
            }
            console.log("vue ele is mounted",options)
        },
    })
}

在用户仓库中存储 按钮 数组数据

src/store/userinfo.ts

ts
// 引入定义小仓库方法
import { defineStore } from 'pinia';
// 本地存储操作token
import { getToken, removeToken, setToken } from '../utils/token-utils';
// state类型的数据类型
import type { UserInfoState } from './interface';
// 消息提示
import { ElMessage } from 'element-plus'
// 静态路由
import { staticRoutes } from '@/router/routes'
// 导入用户相关的API
import { getUserInfo, reqUserLogin, reqUserlogout } from '@/api/user';
// 引入类型
import type {LoginResponseData, UserInfoResponseData} from '@/api/user/type/index'
/**
 * 用户信息
 * @methods setUserInfos 设置用户信息
 */
export const useUserInfoStore = defineStore('userInfo', {

  state: (): UserInfoState => {
    return {
      token: getToken() as string,
      name: '',
      avatar: '',
      menuRoutes: [],
      buttons:[]
    }
  },

  actions: {
    // 登陆
    async login(username: string, password: string) {
      // 定义请求体
      const data = {
        username,
        password
      }
      // 获取响应数据
      const result:LoginResponseData = await reqUserLogin(data)
      // 存储token数据(pinia)
      this.token = result.token
      // 存储token数据(localstore)
      setToken(result.token)
      
    },
    // 获取用户信息(路由鉴权那里进行调用)
    async getInfo() {
      const result:UserInfoResponseData = await getUserInfo()
      // console.log("result ",result)
      // 设置小仓库中的数据状态
      this.name = result.name
      this.avatar = result.avatar
      //存储当前用户拥有哪些按钮权限标识 ['btn.模块的名字.xxxx']
      this.buttons = result.buttons;
      // 设置用户的路由
      this.menuRoutes = staticRoutes
    },
    // 退出登陆
    async reset() {
      // 发送请求
      await reqUserlogout()
      // 删除local中保存的token
      removeToken()
      // 提交重置用户信息的mutation
      this.token = ''
      this.name = ''
      this.avatar = ''
    },
  },
});

在入口文件 注册为全局指令

src/main.ts

ts
import {has} from '@/utils/directive';
//引入自定义指令文件函数
has(app);

在组件中使用:src/views/product/spu/index.vue

vue
<!-- 添加按钮 -->
<el-button
    type="primary"
    :icon="Plus"
    @click="addSpu"
    v-has="'btn.Spu.add'"
    :disabled="categoryStore.c3Id ? false : true">添加SPU
</el-button>

defineProps()

ts

# vue3中 子组件使用 defineProps(["arr"])获取父组件传值,可以直接在模版中使用
<v-chart >{{arr}}<v-chart >
   
# vue3中 子组件使用 defineProps(["arr"])获取父组件传值,可以在JS中需要使用对象的形式获取 
const props = defineProps(["arr"])
// 获取父组件传来的值
props.arr

defineProps(["arr"])

ES6语法

数组的方法使用

ts
删飞改麦

数组reduce方法

ts
reduce执行回调函数的次数是 数组元素长度 - 1

使用reduce方法模拟for循环:
// 设置起始值为一个空数组,执行6次语句,遍历了数组,返回最有一次执行的结果。
arr.reduce((prev,next)=>{
    // 如果元素对象中存在某一个属性,把这个属性的属性值
   	if(next.attrIdAndValueId){
        // 从数组中解构出属性id和属性值id
        const [attrId,valueId] = next.attrIdAndValueId.split(":")
        // 定义一个对象
        const attrIdAndValueIdObj = {
            attrId,
            valueId
        }
        // 向空数组中push追加元素
        prev.push(attrIdAndValueIdObj)
        // 返回数组
        return prev
    }
},[])

pinia语法

pinia函数

pinia中可以使用vue3中的语法

ts
setup,computed,watch等钩子函数都可以在pinia中使用

watch和watchEffect的区别

ts
1 watch能确定监听的数据源,watchEffect不能
2 watch是惰性监听,watchEffect 立即执行
3 watch可以监听到新的值和旧值,watchEffect只能监听到新值

watch的四种写法

ts
1 侦听ref对象
2 侦听数据代理对象
3 侦听一个函数
4 侦听数组

清空仓库中的state数据

ts
import useCategoryStore from '@/store/category'
const categoryStore = useCategoryStore()
onUnmounted(()=>{
    // 重置 state -- 仅在选项式API时可用
    categoryStore.$reset()
})

数据联调

ts
前端和后端需要对接口,对参数