Vue
日期
- 本地时间:不带
Z后缀的时间一般表示本地时间,时间取决于服务器的时区设置。 - UTC 时间:带有
Z的时间表示协调世界时间(UTC)。Z是 “Zulu” 时间的缩写,表示没有时区偏移。 - 如果日期带时间,应采用DateTimeOffset,方便时间统一。
- 与docker环境变量有关 - TZ=Asia/Shanghai
- js中将原date与中国时间协调显示
从后端读来格式为t:"2023-09-07T14:00:00+00:00",修改为t:"2023-09-07 14:00:00"或从后端读来格式为t: "2023-09-07T22:00:00+08:00",转为t: "2023-09-07 14:00:00.000Z",最后再转为t:"2023-09-07 14:00:00"
if (ele.t.endsWith("+00:00") )
ele.t = ele.t.replace("T", " ").split("+")[0];
else
ele.t = new Date(ele.t).toISOString().replace("T", " ").split(".")[0];import { format } from "date-fns";
format(validDate, "yyyy/MM/dd")
# 好用
import { useDateFormat } from "@vueuse/core";
useDateFormat(date, "YYYY/MM/DD"); //'YYYY-MM-DD HH:mm:ss'
vitepress
yarn add -D vitepress@2.0.0-alpha.9
yarn vitepress init
md文件嵌套
可以使用 <!--@include: markdown文件路径-->,在一个 markdown 文件中嵌套另一个 markdown 文件的内容,markdown文件路径 可以是相对路径,也可以是 @/ 开头的以根目录为起点的路径
<!--@include: ./markdown-examples-demo.md-->Echarts
yarn add echarts
1、安装后无法立刻找到模块“echarts”的声明文件,关闭 vc 后,重新打开。
设置 setOption 时,必须要在 dom 加载完之后加载。即在 onMounted 中加载。
不能将类型“null”分配给类型“HTMLElement”,使用!进行处理或者使用 as 进行断言(原来感叹号是非 null 和非 undefined 的类型断言),echarts.init(document.getElementById('main')!);
vuetify图标个别引用
yarn add -D @mdi/js @kingyue/rollup-plugin-modify
//vite.config.ts
import Modify from '@kingyue/rollup-plugin-modify'
import * as mdicons from '@mdi/js'
const mdi: Record<string, string> = {}
Object.keys(mdicons).forEach((key) => {
const value = (mdicons as Record<string, string>)[key]
mdi[
key.replace(
/[A-Z]+(?![a-z])|[A-Z0-9]/g,
($, ofs) => (ofs ? '-' : '') + $.toLowerCase(),
)
] = value
})
plugins: [
Modify({
exclude: ['node_modules/**'],
find: /\b(?<![/\w])(mdi-[\w-]+)\b(?!\.)/,
replace: (match: string) => {
if (mdi[match]) {
return mdi[match]
} else {
console.warn('[plugin-modify] No matched svg icon for ' + match)
return match
}
},
sourcemap: false,
}),
//vuetify.ts
import type { FunctionalComponent } from 'vue'
import { createVuetify, type IconSet, type IconProps } from 'vuetify'
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
function filename(path: string) {
return path
.split(/(\\|\/)/g)
.pop()!
.replace(/\.[^/.]+$/, '')
}
const svgIcons = Object.fromEntries(
Object.entries(
import.meta.glob<FunctionalComponent>('@/assets/icons/*.svg', {
eager: true,
import: 'default',
as: 'component',
}),
).map(([k, v]) => [filename(k), v]),
)
const custom: IconSet = {
component: (props: IconProps) =>
h(props.tag, [h(svgIcons[props.icon as string])]),
}
export default createVuetify({
icons: {
defaultSet: 'mdi',
aliases,
sets: { mdi, custom },
},icon直接显示svg用法
<v-icon icon="custom:shuze" size="4.5em" color="primary" class="mb-4" />
<v-icon icon="custom:shuze2" size="x-large" class="drawer-header-icon" color="primary" />5、node 版本,nvm,改版本时,cmd 要管理员模式
解决 node 升级到 18 版本 node-sass 安装问题 //更改为如下版本 "node-sass": "^8.0.0", "sass-loader": "^10.4.1",
nvm use
vue-next-admin
1、http://shuze.net:8009/api/CommonService/menu/roleMenus
获取用户菜单权限
CurrentUser 无需注入即可使用。
2、http://shuze.net:8009/api/CommonService/menu/userMenuPermission
获取用户的权限
package.json
yarn 交互式更新
yarn upgrade-interactive --latest删除没有用到依赖
# 全局安装
npm install -g depcheck
# 然后在你项目的根目录下执行以下命令:
depcheckvue3+vuetify+vite+pinia+axios
1、安装 vuetify3
yarn create vuetify@latest
yarn add axios
yarn add -D unplugin-vue-components unplugin-auto-import vite-plugin-vue-meta-layouts unplugin-vue-router
yarn add echarts vue-echarts
yarn add lodash-es @types/lodash-es
yarn add @vueuse/core
yarn add -D @mdi/js @kingyue/rollup-plugin-modify vite-svg-loader
yarn add vite-svg-loader2、注意事项
1、package.json中"build": "vue-tsc --noEmit && vite build",==》"build": "vite build",否则导入的插件 .d.ts出错
2、解决找不到'virtual:meta-layouts' module问题, add vite-plugin-vue-meta-layouts/client to compilerOptions.types of your tsconfig3、配置 .env.production 等环境,要设置目录
ENV = 'development'
VITE_VUE_APP_BASE_API = 'http://shuze.net/api'
VITE_VUE_APP_AUTH_API = 'http://shuze.net/auth'vite 插件
// vite.config.js
import ViteRestart from 'vite-plugin-restart'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Pages from 'vite-plugin-pages'
import Layouts from 'vite-plugin-vue-layouts';
export default {
plugins: [
ViteRestart({
restart: [
'my.config.[jt]s',
]
}),
Components({ dts: './src/components.d.ts', types: [] }),
AutoImport({
imports: [
'vue',
'pinia',
'vue-router','@vueuse/head', '@vueuse/core',
],
dts: 'src/types/auto-imports.d.ts',
dirs: ['src/stores'],
}),
Pages({
dirs: 'src/views',
}),
Layouts(),
],
}//env.d.ts
/// <reference types="vite-plugin-pages/client" />
/// <reference types="vite-plugin-vue-layouts/client" />lodash-es
vueuse/core
1、 yarn add @vueuse/core
动画(Animation)—包含易于使用的过渡、超时和计时函数
浏览器(Browser)—可用于不同的屏幕控制、剪贴板、首选项等
组件(Component)— 为不同的组件方法提供简写
Formatters – 提供反应时间格式化功能
传感器(Sensors )—用于监听不同的 DOM 事件、输入事件和网络事件
状态(State )—管理用户状态(全局、本地存储、会话存储)
实用程序(Utility)—不同的实用程序函数,如 getter、条件、引用同步等
Watch —更高级的观察者类型,如可暂停观察者、去抖动观察者和条件观察者
杂项(Misc)— 事件、WebSockets 和 Web Worker 的不同类型的功能useFullscreen
//isFullscreen 当前是否是全屏,true/false,可用于一些逻辑的判断
//toggle 是函数直接调用即可
const { isFullscreen, toggle } = useFullscreen();
<button @click="toggle">点击切换</button>useStorage
import { useStorage } from "@vueuse/core"
const theDefault = {
name: "Banana",
color: "Yellow",
size: "Medium",
count: 0,
}
let state = useStorage("vue-use-local-storage", theDefault)
// state = JSON.parse(state.value)
console.log(state)useClipboard 剪切板功能
<script setup>
import { useClipboard } from "@vueuse/core"
//text 复制的内容
//copy 是复制的函数
// isSupported 判断当前的浏览器是否支持这个api
const input = ref("")
const { text, copy, isSupported } = useClipboard()
console.log(text, copy, isSupported)
</script>
<template>
<div>
<input v-model="input" type="text" />
<button @click="copy(input)">Copy</button>
</div>
</template>useTitle 动态修改标题名
<script setup>
// import { useToggle } from "@vueuse/shared"
import { useTitle } from "@vueuse/core"
const title = useTitle()
console.log(title.value) // print current title
const num = ref(0)
const btn = () => {
num.value++
title.value = "Hello" + num.value // change current title
}
</script>
<template>
<button @click="btn">点击</button>
</template>useDateFormat 时间格式
<script setup>
import { useNow, useDateFormat } from "@vueuse/core"
const formatted = useDateFormat(useNow(), "YYYY-MM-DD")// HH:mm:ss
</script>
<template>
<div>{{ formatted }}</div>
</template>`2、broswer(浏览器相关),点击复制文字:useClipboard 响应式 storage 数据,useStorage 来实现我们的localStorage功能,useStorage()将要用于引用的键名作为第一个参数传递,将要保存的值作为第二个参数传递。可代替 localStorage。当 useStorage 有两个参数的时候,是设置存储信息,并返回格式化好的数据 当 useStorage 只有一个参数的时候,是读取,但是返回的数据需要 json.parse 下,是字符串对象
import { useStorage } from '@vueuse/core'
const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' })
const msg = ref('你好')
//保存值
useStorage('msg',msg.value)
//获取值
const msgData = useStorage('msg')
console.log('msgData',msgData.value)
//删除
const clear = () => {
msg.value = null
}useQRCode 二维码生成器,搭配 qrcode 使用
<script setup>
import { useQRCode } from "../utils/useQrcode"
let inp = ref("")
let qrcode = null
const inpBtn = () => {
qrcode = useQRCode(inp.value)
}
</script>
<template>
<input type="text" v-model="inp" @input="inpBtn" />
<img :src="qrcode" alt="qrcode" />
</template>useVModel,用于父子组件共享数据,
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits(['update:modelValue'])
const data = useVModel(props, 'modelValue', emit)
</script>前端文件上传
<v-col cols="3">
<v-file-input
label="File input"
v-model="state.selectedFile"
></v-file-input> </v-col
><v-col>
<v-btn @click="uploadFile">上传</v-btn></v-col>//modelName,file通通到data中,FormData格式
export function exportFromUpload(data: any) {
return request({
url: "/" + "CommonService" + "/" + "Common" + "/" + "ExportFromUpload",
method: "post",
data,
});
}
function uploadFile() {
const formData = new FormData(); // 创建FormData对象
formData.append("modelName", "Model");
formData.append("file", state.selectedFile);
let modelName = "Model";
exportFromUpload(formData).then((res) => {
console.log(res.data);
});
} public string ExportFromUpload( string modelName , IFormFile file )//
{
var result = ShuzeClassLibrary.ExportFromUpload("CommonService", modelName, file);
return result;
}前端要采用 FormData 对象,增加的 key 要与后端参数名一致,不然获取不到值。多参数时不能用 params 方式。后端采用 IFormFile 格式,可返回序列化 string 到前端。
后端文件下载
export function genFormVue(moduleName: string, modelName: string) {
return request({
url: "/CommonService/ModelProp/GenFormVue",
method: "get",
responseType: "blob",
params: {
moduleName,
modelName,
},
});
} <v-btn @click="genVue">生成vue文件</v-btn>
genVue() {
genFormVue(this.moduleName, this.modelName).then((res) => {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(res.data);
link.download = "item.vue";
link.click();
});
}, public async Task<ActionResult> GenFormVueAsync(int modelType)
{
var stream = new MemoryStream();
var sw = new StreamWriter(stream, Encoding.UTF8);
string sl = "";
sw.WriteLine(sl);
sw.Flush();
stream.Position = 0;
var result = new FileStreamResult(stream, new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/plain"));
result.FileDownloadName = "index.vue";
return result;
}<v-btn @click="exportItem"> <v-icon>mdi-export</v-icon>导出 </v-btn>
exportItem() {
var input = {
maxResultCount: this.options.itemsPerPage,
skipCount: (this.options.page - 1) * this.options.itemsPerPage,
filter: this.search,
};
getExportFile(this.moduleName, this.modelName).then((res) => {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(
new Blob([res.data], { type: "text/plain" })
);
link.download = "export.txt";
link.click();
});
}, public async Task<FileResult> GetExportFile(GetInputDto input, DateTime? startDate = null, DateTime? endDate = null)
{
MemoryStream memoryStream = new MemoryStream();
StreamWriter streamWriter = new StreamWriter(memoryStream, Encoding.UTF8);
string text = "";
streamWriter.WriteLine(text);
streamWriter.Flush();
memoryStream.Position = 0L;
return new FileStreamResult(memoryStream, "application/octet-stream")
{
FileDownloadName = "report.txt"
};
}vue3 模板,菜单
store/app.ts 中 useAppStore 放置菜单,注意路径格式为“/”
text=>title
Vue 文件中引用路径的介绍
1,路径 ./
./当前文件同级目录, ./css/等同于css/
2. 路径 ../
../当前文件上一级目录
3. @
@ 的作用是在你引入模块时,可以使用 @ 代替 /src 目录,避免易错的相对路径。 (注意@不要用在引入css文件路径,有的项目规范不支持)vue3 组件传值之 props 与 attrs 的区别
最近在学习 vue3,整理了一些学习笔记,如果有人看到,并发现我有写的不对的地方,欢迎指正~
用过 vue 组件传值的小伙伴都知道 props 这个属性,而 $attrs 属性可以看做 props 的加强版,用来简化 vue 组件传值,那么这两个属性具体有什么区别呢?
先说结论,区别如下:
1、props 要先声明才能取值,attrs 不用先声明
2、props 声明过的属性,attrs 里不会再出现
3、props 不包含事件,attrs 包含
4、props 支持 string 以外的类型,attrs 只有 string 类型
混入
//全局混入时,组件内无法调用混入 data 内数据。但组件内混入时,则可以调用混入 data 内数据,20210218 3、相关的解释 3.1 当在组件中和混入中有相同的‘testMix’这个数据时,显示组件中’testMix’对应的数据,当只用混入中有’testMix‘函数时,显示混入中’testMix’对应的数据。 3.2 在组件中和混入中有相同的函数 mixinsFun()时,在组件中调用时,调用的是组件中的 mixinsFun()函数,当只用混入中有 mixinsFun()函数时,在组件中调用 mixinsFun()是调用混入中的。 3.3 在组件中和混入中有相同的 computed 函数 testMix2()时,在组件中调用时,调用的是组件中的 testMix2()函数,当只用混入中有 computed 函数 testMix2()时,在组件中调用 testMix2()是调用混入中的。 3.4 在组件中和混入中有相同的 created()函数时,先执行混入中的 created,再执行组件中的 created。猜想其他生命周期也应该是一样。 3.5 在组件中和混入中有相同的 watch()函数 testMix 时,先执行混入中 watch 的 testMix,再执行组件中 watch 的 testMix。
watch
Vue 之 watch 监听对象中某个属性的方法,vue2 通过 'userinfo.job' 的方式实现(推荐),备注:因为方法 2 是使用字符串作为键来对对象中的内容进行监听,编译器无法识别检测语法,在 vue3 中已经废弃,可以使用新的响应性 api watch和watchEffect来替代该方法
node 版本管理
nvm
nvm install latest 安装最新版本node.js
nvm use 版本号 使用某一具体版本,例如 :nvm use 14.3.0
nvm list 列出当前已安装的所有版本
nvm ls 列出当前已安装的所有版本
nvm uninstall 版本号 卸载某一具体版本,例如:nvm use 14.3.0
nvm ls-remote Mac版本中,列出全部可以安装的node版本
nvm ls available windows版本,列出全部可以安装的node版本
nvm current 显示当前的版本
nvm alias 给不同的版本号添加别名
nvm unalias 删除已定义的别名
nvm reinstall-packages 在当前版本node环境下,重新全局安装指定版本号的npm包vuetify
1、安装
yarn create vuetify
输入项目名vueclient,选取安装插件vueroute pania
进入目录:cd vueclient
运行:yarn dev2、yarn add axios
3、新建环境变量
vite 环境变量文件放在根目录,其中.env.development 文件(npm run dev 时自动启用该文件)和.env.production(npm run bulid 时自动启用该文件)文件为必须。注意:只有以 VITE_ 开头的变量才会被识别,并启用。
js 二进制转十进制
这个是把字符串(只能由字母和数字组成),这个只能是由低进制转高进制
Number.parseInt(string , radix)
这个函数只能将十进制数字转换为任意进制的字符串形式,同样,radix 表示进制,取值 2~36
Number.toString(radix)
defineModel
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。在对话框中使用方便,无需 emit("closeDialog")及 pinia。
#子组件
const flagDialog = defineModel();
const props = defineProps(["id"]);
#父组件(要用v-model:flagDialog)
<ItemDialog
ref="refDialog"
v-model="state.tableInform.flagDialog"
:id="state.fmdata.id"
@refreshItem="refreshItem"
></ItemDialog>