配置
tailwind.config.js
const colors = require("tailwindcss/colors");
const defaultTheme = require("tailwindcss/defaultTheme");
// 引入依赖
// colors:Tailwind CSS 提供的颜色对象。
// defaultTheme:Tailwind CSS 默认的主题对象
const navyColor = {
50: "#E7E9EF",
100: "#C2C9D6",
200: "#A3ADC2",
300: "#697A9B",
400: "#5C6B8A",
450: "#465675",
500: "#384766",
600: "#313E59",
700: "#26334D",
750: "#222E45",
800: "#202B40",
900: "#192132",
};
// 自定义颜色
const customColors = {
navy: navyColor,
"slate-150": "#E9EEF5",
primary: colors.indigo["600"],
"primary-focus": colors.indigo["700"],
"secondary-light": "#ff57d8",
secondary: "#F000B9",
"secondary-focus": "#BD0090",
"accent-light": colors.indigo["400"],
accent: "#5f5af6",
"accent-focus": "#4d47f5",
info: colors.sky["500"],
"info-focus": colors.sky["600"],
success: colors.emerald["500"],
"success-focus": colors.emerald["600"],
warning: "#ff9800",
"warning-focus": "#e68200",
error: "#ff5724",
"error-focus": "#f03000",
};
// 定义了名为navy的颜色调色板。
// 导出配置
module.exports = {
// TailwindCSS应扫描的文件路径,以提取类名并生成最终的CSS文件
content: [
"./src/**/*.{php,html,js,jsx,ts,tsx,vue}",
"./resources/**/*.{php,html,js,jsx,ts,tsx,vue}",
"./storage/framework/views/*.php",
],
darkMode: "class",
// 设置暗模式为通过类名控制,即根元素上添加 class="dark" 来切换暗模式。
theme: {
extend: {
fontFamily: { // 扩展默认的字体族,添加 Poppins 和 Inter 字体
sans: ["Poppins", ...defaultTheme.fontFamily.sans],
inter: ["Inter", ...defaultTheme.fontFamily.sans],
},
fontSize: { // 定义新的字体大小
tiny: ["0.625rem", "0.8125rem"],
"tiny+": ["0.6875rem", "0.875rem"],
"xs+": ["0.8125rem", "1.125rem"],
"sm+": ["0.9375rem", "1.375rem"],
},
colors: { ...customColors }, // 扩展默认的颜色,添加自定义颜色
opacity: { // 定义新的透明度值
15: ".15",
},
spacing: { // 定义新的间距值
4.5: "1.125rem",
5.5: "1.375rem",
18: "4.5rem",
},
boxShadow: { // 定义新的阴影效果
soft: "0 3px 10px 0 rgb(48 46 56 / 6%)",
"soft-dark": "0 3px 10px 0 rgb(25 33 50 / 30%)",
},
zIndex: { // 定义新的 z-index 值
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
},
keyframes: { // 定义新的关键帧动画
"fade-out": {
"0%": {
opacity: 1,
visibility: "visible",
},
"100%": {
opacity: 0,
visibility: "hidden",
},
},
},
},
},
corePlugins: { // 禁用一些默认的核心插件,以减少生成的 CSS 文件大小
textOpacity: false,
backgroundOpacity: false,
borderOpacity: false,
divideOpacity: false,
placeholderOpacity: false,
ringOpacity: false,
},
plugins: [
require("@tailwindcss/line-clamp")
// 引入 @tailwindcss/line-clamp 插件,用于控制文本的行数。
],
};
webpack.mix.js
const mix = require("laravel-mix");
// 引入 laravel-mix 模块,用于配置和运行 Webpack 构建任务
mix
.js("src/js/app.js", "js") // src/js/app.js编译并输出到dist/js目录
.postCss("src/css/app.css", "css", [
require("tailwindcss"),
require("autoprefixer"),
])
// 使用PostCSS处理src/css/app.css文件,
// 并应用tailwindcss和autoprefixer插件。
// 输出结果到dist/css目录下
.options({ processCssUrls: false })
// 禁用CSS URL处理,防止Mix自动处理CSS中的URL
.webpackConfig({
module: {
rules: [ // 使用source-map-loader预处理.js文件,以生成源映射文件
{
test: /\.js$/,
enforce: "pre", // 在其他加载器之前执行
use: ["source-map-loader"],
},
],
},
devServer: {
open: true, // 在开发服务器启动时自动打开浏览器
},
})
.copyDirectory("src/html/", "dist")
// src/html 目录下的所有文件复制到 dist 目录下
.copyDirectory("src/images", "dist/images")
// src/images 目录下的所有文件复制到 dist/images 目录下
.copyDirectory("src/fonts", "dist/fonts")
// src/fonts 目录下的所有文件复制到 dist/fonts 目录下
.setPublicPath("dist")
// 设置输出文件的公共路径为 dist
.disableNotifications();
// 禁用 Mix 在构建成功或失败时发送的通知
JS
app.js
import Alpine from "alpinejs";
// AlpineJS:一个轻量级的JavaScript框架,用于构建交互式用户界面
// AlpineJS Plugins
import persist from "@alpinejs/persist";
// persist 用于持久化数据
import collapse from "@alpinejs/collapse";
// collapse实现折叠效果
import intersect from "@alpinejs/intersect";
// intersect 用于检测元素是否进入视口
// 导入第三方库
/**
* Scrollbar Library
* @see https://github.com/Grsmto/simplebar
*/
import SimpleBar from "simplebar";
// SimpleBar:一个滚动条库,用于自定义滚动条样式。
/**
* Code highlighting library
* JUST FOR DEMO PURPOSE ONLY FOR HIGHLIGHTING CODE
* @see https://highlightjs.org/
*/
// highlight.js:一个代码高亮库,用于在网页上显示高亮的代码块
import hljs from "highlight.js/lib/core";
import xml from "highlight.js/lib/languages/xml";
/**
* Date Utility Library
* @see https://day.js.org/
*/
import dayjs from "dayjs";
// dayjs:一个轻量级的日期处理库
/**
* Carousel Library
* @see https://swiperjs.com/
*/
import Swiper from "swiper/bundle";
// Swiper:一个滑动库,用于实现轮播图效果
/**
* Drag & Drop Library
* @see https://github.com/SortableJS/Sortable
*/
import Sortable from "sortablejs";
// Sortable:一个拖放排序库,用于实现列表项的拖放排序
/**
* Chart Library
* @see https://apexcharts.com/
*/
import ApexCharts from "apexcharts";
// ApexCharts:一个图表库,用于绘制各种图表。
/**
* Table Library
* @see https://gridjs.io/
*/
import * as Gridjs from "gridjs";
// Gridjs:一个表格库,用于创建响应式的表格
// Forms Libraries
import "@caneara/iodine"; // @see https://github.com/caneara/iodine
// @caneara/iodine:一个表单验证库
import * as FilePond from "filepond";
// @see https://pqina.nl/filepond/
// FilePond:一个文件上传库,用于处理文件上传
import FilePondPluginImagePreview from "filepond-plugin-image-preview"; // @see https://pqina.nl/filepond/docs/api/plugins/image-preview/
// FilePondPluginImagePreview:FilePond的插件,用于预览上传的图片。
import Quill from "quill/dist/quill.min"; // @see https://quilljs.com/
// Quill:一个富文本编辑器库
import flatpickr from "flatpickr"; // @see https://flatpickr.js.org/
// flatpickr:一个日期选择器库
import Tom from "tom-select/dist/js/tom-select.complete.min"; // @see https://tom-select.js.org/
// Tom:一个选择器库,用于创建多选和单选选择器
// Helper Functions
import * as helpers from "./utils/helpers";
// helpers:自定义的辅助函数集合,用于处理各种通用任务
// Pages Scripts
import * as pages from "./pages";
// pages:包含各个页面的特定脚本
// Global Store
import store from "./store";
// store:全局状态管理库,用于存储应用的状态
// Breakpoints Store
import breakpoints from "./utils/breakpoints";
// breakpoints:用于存储不同屏幕尺寸的断点信息
// Alpine Components
import usePopper from "./components/usePopper";
// usePopper,用于实现弹出提示
import accordionItem from "./components/accordionItem";
// accordionItem, 实现手风琴效果
import navLink from "./components/navLink";
// navLink, 实现导航链接
// Alpine Directives
import tooltip from "./directives/tooltip";
// tooltip, 实现工具提示
import inputMask from "./directives/inputMask";
// inputMask,实现输入掩码
// Alpine Magic Functions
import notification from "./magics/notification";
// notification, 显示通知
import clipboard from "./magics/clipboard";
// clipboard, 复制文本到剪贴板
// Register HTML, XML language for highlight.js
// Just for demo purpose only for highlighting code
hljs.registerLanguage("xml", xml);
// 注册XML语言支持
hljs.configure({ ignoreUnescapedHTML: true });
// 配置highlight.js忽略未转义的HTML
// Register plugin image preview for filepond
FilePond.registerPlugin(FilePondPluginImagePreview);
// 注册FilePond的图像预览插件
// 挂载库和工具到全局对象window
window.hljs = hljs;
window.dayjs = dayjs;
window.SimpleBar = SimpleBar;
window.Swiper = Swiper;
window.Sortable = Sortable;
window.ApexCharts = ApexCharts;
window.Gridjs = Gridjs;
window.FilePond = FilePond;
window.flatpickr = flatpickr;
window.Quill = Quill;
window.Tom = Tom;
window.Alpine = Alpine;
window.helpers = helpers;
window.pages = pages;
// 注册AlpineJS的persist、collapse、intersect插件
Alpine.plugin(persist);
Alpine.plugin(collapse);
Alpine.plugin(intersect);
// 注册AlpineJS指令
Alpine.directive("tooltip", tooltip);
Alpine.directive("input-mask", inputMask);
// 注册自定义的AlpineJS魔法函数(notification、clipboard)
Alpine.magic("notification", () => notification);
Alpine.magic("clipboard", () => clipboard);
// 注册AlpineJS数据组件
Alpine.store("breakpoints", breakpoints);
Alpine.store("global", store);
Alpine.data("usePopper", usePopper);
Alpine.data("accordionItem", accordionItem);
Alpine.data("navLink", navLink);
store.js
代码分析
export default {
isDarkModeEnabled: false, // 是否启用暗模式
isMonochromeModeEnabled: false, // 是否启用单色模式
isSearchbarActive: false, // 搜索栏是否激活
isSidebarExpanded: false, // 左侧侧边栏是否展开
isRightSidebarExpanded: false, // 右侧侧边栏是否展开
currentLocation: currentLocation(),
// 当前页面的URL,通过调用 currentLocation() 函数获取
init() {
let firstTime = true; // 标记是否是第一次初始化
this.isDarkModeEnabled = Alpine.$persist(false).as("_x_darkMode_on");
// 使用 Alpine.$persist 持久化暗模式状态
this.isSidebarExpanded =
document.querySelector(".sidebar") &&
document.body.classList.contains("is-sidebar-open") &&
Alpine.store("breakpoints").xlAndUp;
// 根据页面初始状态和断点设置侧边栏是否展开
Alpine.effect(() => { // 监听状态变化并动态更新DOM类名
this.isDarkModeEnabled
? document.documentElement.classList.add("dark")
: document.documentElement.classList.remove("dark");
});
// 监听isDarkModeEnabled,更新document.documentElement的dark类名
Alpine.effect(() => {
this.isMonochromeModeEnabled
? document.body.classList.add("is-monochrome")
: document.body.classList.remove("is-monochrome");
});
// 监听isMonochromeModeEnabled,更新document.body的is-monochrome类名
Alpine.effect(() => {
this.isSidebarExpanded
? document.body.classList.add("is-sidebar-open")
: document.body.classList.remove("is-sidebar-open");
});
// 监听isSidebarExpanded,更新document.body的is-sidebar-open类名
Alpine.effect(() => {
if (Alpine.store("breakpoints").name && !firstTime) {
this.isSidebarExpanded = false;
this.isRightSidebarExpanded = false;
}
});
// 监听断点变化,调整侧边栏和右侧边栏的状态
Alpine.effect(() => {
if (Alpine.store("breakpoints").smAndUp) this.isSearchbarActive = false;
});
// 监听断点变化,调整搜索栏的状态
firstTime = false;
},
documentBody: {
["@load.window"]() { // 页面加载时触发的事件
const preloader = document.querySelector(".app-preloader");
// 查找页面中的预加载器元素
// 如找到预加载器,使用setTimeout延迟执行动画效果,150毫秒后添加动画类名,1000毫秒后移除预加载器
if (!preloader) return;
setTimeout(() => {
preloader.classList.add(
"animate-[cubic-bezier(0.4,0,0.2,1)_fade-out_500ms_forwards]"
);
setTimeout(() => preloader.remove(), 1000);
}, 150);
},
},
};
function currentLocation() {
return [location.protocol, "//", location.host, location.pathname].join("");
// 通过拼接protocol、host 和 pathname,返回当前页面的完整URL.
}
Alpine.effect方法
Alpine.effect是Alpine.js提供的一个响应式机制,用于在数据发生变化时自动执行某些操作。它类似于Vue.js中的watch或React中的useEffect钩子。
Alpine.effect接受一个函数作为参数,在依赖的数据发生变化时被重新执行。 可在这个函数中进行DOM操作或其他逻辑处理
utils/helper.js
代码分析
export function toggleCode(e) {
// 切换代码块的显示状态 e:事件对象,通常是由点击事件触发
const card = e.target.closest(".card");
// 使用closest方法找到事件目标元素的最近的祖先元素.card 容器
const codeWrapper = card.querySelector(".code-wrapper");
// 在 .card 容器中查找 .code-wrapper 元素
e.target.checked
? codeWrapper.classList.remove("hidden")
: codeWrapper.classList.add("hidden");
/*
如果 checked 为 true,则移除 .code-wrapper 元素的 hidden 类,使其可见
如果 checked 为 false,则添加 .code-wrapper 元素的 hidden 类,使其隐藏
*/
}
export function getBrwoserScrollbarWidth() {
// 获取浏览器滚动条的宽度
return window.innerWidth - document.documentElement.clientWidth;
/* window.innerWidth 浏览器窗口的内部宽度,包括滚动条
document.documentElement.clientWidth:文档元素的宽度,不包括滚动条
两者相减得到滚动条的宽度
*/
}
utils/breakpoints
代码分析
// 响应屏幕尺寸变化,检测当前屏幕的断点,并根据断点更新一些属性
export default {
init() {
this.update(); // 调用update方法,初始化当前的窗口高度和宽度
window.addEventListener("resize", this.onResize.bind(this), {
passive: true,
});
// 添加一个resize事件监听器,当窗口大小变化时调用onResize方法
},
height: 0,
width: 0,
name: "", // 当前断点的名称
isBreakpointChanged: false, // 是否断点发生变化(但未在代码中使用)
isXs: false,
isSm: false,
isMd: false,
isLg: false,
isXl: false,
is2xl: false,
/* isXs, isSm, isMd, isLg, isXl, is2xl: 分别表示当前窗口是否处于 xs, sm, md, lg, xl, 2xl 断点
*/
smAndDown: false,
smAndUp: false,
mdAndDown: false,
mdAndUp: false,
lgAndDown: false,
lgAndUp: false,
xlAndDown: false,
xlAndUp: false,
/* smAndDown, smAndUp, mdAndDown, mdAndUp, lgAndDown, lgAndUp, xlAndDown, xlAndUp: 表示当前窗口是否处于某个断点及其以下或以上
*/
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
the2xl: 1536,
/*
sm, md, lg, xl, the2xl: 分别表示 sm, md, lg, xl, 2xl 断点的像素值
*/
update() {
const height = window.innerHeight;
const width = window.innerWidth;
// 获取当前窗口的高度和宽度
const xs = width < this.sm;
const sm = width < this.md && !xs;
const md = width < this.lg && !(sm || xs);
const lg = width < this.xl && !(md || sm || xs);
const xl = width < this.the2xl && !(lg || md || sm || xs);
const the2xl = width >= this.the2xl;
this.height = height;
this.width = width;
this.isXs = xs;
this.isSm = sm;
this.isMd = md;
this.isLg = lg;
this.isXl = xl;
this.is2xl = the2xl;
this.smAndDown = (xs || sm) && !(md || lg || xl || the2xl);
this.smAndUp = !xs && (sm || md || lg || xl || the2xl);
this.mdAndDown = (xs || sm || md) && !(lg || xl || the2xl);
this.mdAndUp = !(xs || sm) && (md || lg || xl || the2xl);
this.lgAndDown = (xs || sm || md || lg) && !(xl || the2xl);
this.lgAndUp = !(xs || sm || md) && (lg || xl || the2xl);
this.xlAndDown = (xs || sm || md || lg || xl) && !the2xl;
this.xlAndUp = !(xs || sm || md || lg) && (xl || the2xl);
if (xs) this.name = "xs";
if (sm) this.name = "sm";
if (md) this.name = "md";
if (lg) this.name = "lg";
if (xl) this.name = "xl";
if (the2xl) this.name = "2xl";
document.documentElement.style.setProperty("--vh", `${height * 0.01}px`);
/* --vh 是一个CSS自定义属性(变量)。自定义属性以--开头,可在CSS中定义并在其他地方引用.
将当前窗口高度的 1% 作为值,设置到根元素的 --vh 自定义属性中
*/
},
onResize() {
clearTimeout(this.resizeTimeout);
// 清除之前的定时器,防止多次调用 update 方法
this.resizeTimeout = window.setTimeout(this.update.bind(this), 175);
// 设置一个新的定时器,在 175 毫秒后调用 update 方法
},
};
pages/index.js
代码分析
export { charts } from "./apexchartDemo";
export { tomSelect } from "./tomselectDemo";
export * as tables from "./tablesDemo";
export * as formValidation from "./formValidationDemo";
export { default as initCreditCard } from "./initCreditCard";
/*
负责从其他模块导出一些功能或对象,具体如下:
charts 和 tomSelect 分别从 apexchartDemo.js 和 tomselectDemo.js 模块中导出。
tables 和 formValidation 作为命名空间从 tablesDemo.js 和 formValidationDemo.js 模块中导出所有成员。
initCreditCard 从 initCreditCard.js 模块中默认导出。
*/
components/usePopper.js
代码分析
import { createPopper } from "@popperjs/core";
// 从 @popperjs/core中导入createPopper函数,用于创建Popper实例
export default (userOptions = {}) => ({
popperInstance: null,
// 存储 Popper 实例的变量,初始值为 null
options: buildOptions(userOptions),
// 通过buildOptions函数根据用户提供的userOptions构建的Popper配置选项
isShowPopper: false,
// Popper 是否显示,默认为 false
init() {
this.$nextTick( //确保DOM已经渲染完成
() =>
(this.popperInstance = createPopper(
this.$refs.popperRef,
this.$refs.popperRoot,
this.options
))
// 创建Popper实例,并将其赋值给popperInstance
);
/*
$refs可用于检索组件内标记有x-ref的DOM元素。
*/
this.$watch("isShowPopper", (val) => val && this.popperInstance.update());
/* 监听isShowPopper,为true时调用popperInstance.update()更新 Popper 的位置
*/
Alpine.effect(
() => Alpine.store("breakpoints").name && (this.isShowPopper = false)
);
// 监听断点变化,当断点变化时设置isShowPopper为false,隐藏Popper
},
});
const buildOptions = (options) => {
const config = {
placement: options.placement ?? "auto",
// 位置,默认为 auto
strategy: options.strategy ?? "fixed",
// 定位策略,默认为 fixed
onFirstUpdate: options.onFirstUpdate ?? function () {},
// 第一次更新时的回调函数,默认为空函数
modifiers: [ // 修饰符数组,包含一个 offset 修饰符,用于设置偏移量
{
name: "offset",
options: {
offset: [0, options.offset ?? 0],
},
},
],
};
if (options.modifiers) config.modifiers.push(...options.modifiers);
// 如果提供了额外的 modifiers,将其合并到配置对象中
return config;
};
components/accordionItem.js
代码分析
export default (id) => ({
acc_id: id,
get expanded() {
return this.expandedItem === this.acc_id;
},
/* 当前对象是否处于展开状态。如果 expandedItem 等于 acc_id,则返回 true,否则返回 false
*/
set expanded(val) {
this.expandedItem = val ? this.acc_id : null;
}
/*
设置当前对象的展开状态。如果 val 为 true,则将 expandedItem 设置为 acc_id;否则将其设置为 null。
*/
});
components/navLink.js
代码分析
export default () => ({
isActive: false,
init() {
if (!this.$el.href) return;
// 检查元素是否有 href 属性,如果没有则直接返回
this.isActive =
this.$el.href.split("?")[0].split("#")[0] ===
this.$store.global.currentLocation;
/*
比较 href 的基础部分(去除查询参数和锚点)与全局存储中的 currentLocation 是否相同
如果相同,则设置 isActive 为 true,并根据条件展开元素,使元素滚动到视图中心
*/
if (this.isActive) {
if (this.acc_id) this.expanded = true;
this.$el.scrollIntoView({ block: "center" });
}
},
});
/*
scrollIntoView 方法的选项对象可以包含多个参数,用于控制滚动行为。
除了 { block: "center" } 之外,还有其他一些常用的参数。
常用参数:
block:
start:元素的顶部边缘与视口的顶部边缘对齐。
center:元素的顶部边缘与视口的中间位置对齐。
end:元素的底部边缘与视口的底部边缘对齐。
nearest:元素的最近边缘与视口的最近边缘对齐。
inline:
start:元素的左边缘与视口的左边缘对齐。
center:元素的左边缘与视口的中间位置对齐。
end:元素的右边缘与视口的右边缘对齐。
nearest:元素的最近边缘与视口的最近边缘对齐。
behavior:
auto:默认行为,立即滚动。
smooth:平滑滚动,滚动过程会有动画效果
*/
directives/tooltip.js
代码分析
import tippy, { followCursor, roundArrow } from "tippy.js";
/* 导入tippy.js库及其插件followCursor和roundArrow。
tippy: 主要的提示框组件。
followCursor: 插件,使提示框跟随鼠标光标。
roundArrow: 插件,为提示框添加圆角箭头
*/
export default (el, { modifiers, expression }, { evaluateLater, effect }) => {
const getContent = evaluateLater(expression);
const options = buildOptionsFromModifiers(modifiers);
effect(() => { /* 初始执行一次,并且每当其依赖的数据发生变化时,会自动重新执行 */
getContent((content) => {
if (options.content === true) {
options.content = document
.querySelector(content)
.content.cloneNode(true);
options.allowHTML = true;
el.__x_tippy = tippy(el, options);
} else {
if (!el.__x_tippy) {
el.__x_tippy = tippy(el, options);
}
el.__x_tippy.setContent(content);
}
});
});
/*
如options.content为true,则从DOM中查询并克隆内容,设置为提示框的内容。
否则,直接设置提示框的内容为 content
*/
};
const buildOptionsFromModifiers = (modifiers) => {
/*
从给定的修饰符列表中构建tippy.js提示框的配置选项。
这些修饰符可以控制提示框的各种行为和样式.
*/
const options = {
plugins: [], // 存储插件的数组,默认为空
arrow: roundArrow, // 使用roundArrow插件来生成箭头
animation: "shift-away", // 动画效果,默认为 "shift-away"
zIndex: 10003, // 层叠顺序,默认为 10003
};
if (modifiers.includes("duration")) {
options.duration = parseInt(modifiers[modifiers.indexOf("duration") + 1]);
}
/*
如果修饰符列表中包含 "duration",则从下一个位置获取持续时间值,并将其转换为整数后赋值给 options.duration
*/
if (modifiers.includes("delay")) {
options.delay = parseInt(modifiers[modifiers.indexOf("delay") + 1]);
}
/*
如果修饰符列表中包含 "delay",则从下一个位置获取延迟时间值,并将其转换为整数后赋值给 options.delay
*/
if (modifiers.includes("cursor")) {
/*如果修饰符列表中包含 "cursor",则将 followCursor 插件添加到 plugins 数组中
*/
options.plugins.push(followCursor);
const next = modifiers[modifiers.indexOf("cursor") + 1] ?? null;
if (["x", "y", "initial"].includes(next)) {
if (next === "x") options.followCursor = "horizontal";
if (next === "y") options.followCursor = "vertical";
if (next === "initial") options.followCursor = "initial";
} else {
options.followCursor = true;
}
/*
从下一个位置获取方向值("x"、"y" 或 "initial"),并根据值设置 options.followCursor
如果没有指定方向值,则默认设置为 true
*/
}
if (modifiers.includes("on")) {
options.trigger = modifiers[modifiers.indexOf("on") + 1];
}
/*
如果修饰符列表中包含 "on",则从下一个位置获取触发事件类型(如 "click"、"hover" 等),并赋值给 options.trigger
*/
if (modifiers.includes("arrowless")) {
options.arrow = false;
}
/*
如果修饰符列表中包含 "arrowless",则将 options.arrow 设置为 false,表示不显示箭头
*/
if (modifiers.includes("interactive")) {
options.interactive = true;
}
/*
如果修饰符列表中包含 "interactive",则将 options.interactive 设置为 true,表示提示框可交互
*/
if (modifiers.includes("border") && options.interactive) {
options.interactiveBorder = parseInt(
modifiers[modifiers.indexOf("border") + 1]
);
}
/*
如果修饰符列表中包含 "border" 且 options.interactive 为 true,则从下一个位置获取边框宽度值,并将其转换为整数后赋值给 options.interactiveBorder
*/
if (modifiers.includes("debounce") && options.interactive) {
options.interactiveDebounce = parseInt(
modifiers[modifiers.indexOf("debounce") + 1]
);
}
/*
如果修饰符列表中包含 "debounce" 且 options.interactive 为 true,则从下一个位置获取防抖时间值,并将其转换为整数后赋值给 options.interactiveDebounce
*/
if (modifiers.includes("max-width")) {
options.maxWidth = parseInt(modifiers[modifiers.indexOf("max-width") + 1]);
}
/*
如果修饰符列表中包含 "max-width",则从下一个位置获取最大宽度值,并将其转换为整数后赋值给 options.maxWidth
*/
if (modifiers.includes("placement")) {
options.placement = modifiers[modifiers.indexOf("placement") + 1];
}
/*
如果修饰符列表中包含 "placement",则从下一个位置获取放置位置(如 "top"、"bottom" 等),并赋值给 options.placement
*/
if (modifiers.includes("light")) options.theme = "light";
if (modifiers.includes("primary")) options.theme = "primary";
if (modifiers.includes("secondary")) options.theme = "secondary";
if (modifiers.includes("info")) options.theme = "info";
if (modifiers.includes("success")) options.theme = "success";
if (modifiers.includes("warning")) options.theme = "warning";
if (modifiers.includes("error")) options.theme = "error";
if (modifiers.includes("sm")) options.theme = `${options.theme} is-sm`;
/*
根据不同的主题修饰符(如 "light"、"primary" 等),设置options.theme。
如果包含 "sm" 修饰符,则在当前主题基础上追加 is-sm
*/
if (modifiers.includes("content")) {
options.theme = "content";
options.content = true;
}
/*
如果修饰符列表中包含 "content",则将 options.theme 设置为 "content",并将 options.content 设置为 true
*/
return options;
};
directives/inputMask.js
代码分析
import Cleave from 'cleave.js/dist/cleave.min'
/*
Cleave库是一个用于输入格式化的JavaScript库。它的主要功能包括:
自动格式化输入:根据预设的规则自动格式化用户输入的内容。例如,电话号码、日期、信用卡号等。
实时验证:在用户输入过程中实时验证输入内容,确保输入符合预期格式。
灵活配置:通过配置选项,可以轻松地定制不同的输入格式和验证规则。
*/
export default (el, { expression}, {evaluateLater, effect}) => {
const getContent = evaluateLater(expression)
effect(() => {
getContent(content => {
if (typeof content == 'object')
el.__x_cleave = new Cleave(el, content)
else console.warn('Input mask config should be object')
})
})
}
magics/notification.js
代码分析
import Toastify from "toastify-js";
// 导入了 Toastify 模块,该模块用于创建和显示通知消息
export default (userOptions) => {
/* 定义导出函数
接受一个参数 userOptions,包含用户传递的各种配置选项。
*/
let [position = "right", gravity = "bottom"] = userOptions.position
? userOptions.position.split("-")
: "";
/*
解析出 position 属性,如果存在,则将其按-分割为两个部分,分别赋值给 position 和 gravity。如果 position 不存在,则默认值分别为 "right" 和 "bottom"。
*/
let removeTrigger = null;
// 初始化变量 removeTrigger,用于存储移除通知的触发器元素
const options = {
// 设置默认选项
duration: userOptions.duration || 5000,
gravity,
position,
text: userOptions.text || "This is a message",
newWindow: true,
close: userOptions.hasCloseBtn || false,
backgroundColor: "",
className: userOptions.class || "",
stopOnFocus: userOptions.pauseOnHover || true,
};
/*
duration: 通知显示的时间,默认为 5000 毫秒。
gravity: 通知的垂直位置,从 userOptions 中解析得到。
position: 通知的水平位置,从 userOptions 中解析得到。
text: 通知的文本内容,默认为 "This is a message"。
newWindow: 是否在新窗口中打开链接,默认为 true。
close: 是否显示关闭按钮,默认为 false。
backgroundColor: 通知的背景颜色,默认为空字符串。
className: 自定义的类名,默认为空字符串。
stopOnFocus: 当鼠标悬停时是否暂停计时,默认为 true。
*/
if (userOptions.link) {
options.destination = userOptions.link;
}
/*
检查 userOptions 中是否存在 link 属性,如果存在,则将其赋值给 options.destination,用于指定通知链接的目标地址
*/
if (userOptions.variant) {
options.className = `${options.className} ${userOptions.variant}`;
}
/*
检查 userOptions 中是否存在 variant 属性,如果存在,则将其添加到 options.className 中,用于应用不同的样式变体
*/
if (userOptions.content) {
options.node = document.querySelector(userOptions.content).content.firstElementChild.cloneNode(true);
options.className += " html";
options.close = false;
}
/*
检查 userOptions 中是否存在 content 属性,如果存在,则从指定的选择器中获取内容,并将其克隆为 options.node。同时,将 html 类名添加到 options.className 中,并将 options.close 设置为 false,表示不显示关闭按钮
*/
const toastify = Toastify(options);
toastify.showToast();
/*
使用 Toastify 构造函数创建一个通知实例,并调用 showToast 方法显示通知
*/
if (userOptions.content) {
removeTrigger = toastify.toastElement.querySelector(
"[data-notification-remove]"
);
if (removeTrigger) {
removeTrigger.addEventListener("click", () =>
toastify.removeElement(toastify.toastElement)
);
}
}
/*
检查 userOptions 中是否存在 content 属性,如果存在,则从通知元素中查找带有 data-notification-remove 属性的元素。如果找到该元素,则为其添加点击事件监听器,当点击时调用 removeElement 方法移除通知
*/
};
magics/clipboard.js
代码分析
export default (userOptions) => {
const options = {
content: userOptions.content,
onSuccess: userOptions.success ?? function () {},
onError: userOptions.error ?? function () {},
};
/*
content: 需要复制的文本内容,从 userOptions 中获取。
onSuccess: 成功回调函数,如果 userOptions 中没有提供,则使用一个空函数作为默认值。
onError: 错误回调函数,如果 userOptions 中没有提供,则使用一个空函数作为默认值。
*/
if (userOptions.content === "") return;
if (typeof userOptions.content === "function") {
userOptions.content = userOptions.content();
}
/*
检查 userOptions.content 是否是一个函数。如果是函数,则调用该函数,并将返回值赋值给 userOptions.content
*/
navigator.clipboard.writeText(options.content).then(
function () {
options.onSuccess();
},
function (err) {
options.onError(err);
}
);
/*
使用 navigator.clipboard.writeText 方法将 options.content 复制到剪贴板。该方法返回一个 Promise,成功时调用 options.onSuccess 回调函数,失败时调用 options.onError 回调函数,并传递错误信息 err
*/
};