feat: add interactive demos for AI history, Auth design, and Git intro

This commit is contained in:
sanbuphy
2026-01-19 11:25:10 +08:00
parent bb28f010e3
commit 7d86ba9504
55 changed files with 12984 additions and 5776 deletions
@@ -0,0 +1,134 @@
// auth-design 公共组件配置
// 生成按钮类名
export const getButtonClasses = (variant = 'primary', disabled = false, size = 'medium') => {
const base = 'auth-demo-btn'
const classes = [base]
// 变体
classes.push(`${base}-${variant}`)
// 状态
if (disabled) classes.push(`${base}-disabled`)
// 大小
classes.push(`${base}-${size}`)
return classes.join(' ')
}
// 生成卡片类名
export const getCardClasses = (variant = 'default', clickable = false) => {
const base = 'auth-demo-card'
const classes = [base]
if (variant !== 'default') {
classes.push(`${base}-${variant}`)
}
if (clickable) {
classes.push(`${base}-clickable`)
}
return classes.join(' ')
}
// 生成状态徽章类名
export const getBadgeClasses = (type = 'info') => {
const types = {
success: 'auth-badge-success',
warning: 'auth-badge-warning',
danger: 'auth-badge-danger',
info: 'auth-badge-info',
purple: 'auth-badge-purple'
}
return types[type] || types.info
}
// 生成进度条类名
export const getProgressClasses = (variant = 'primary') => {
return `auth-progress auth-progress-${variant}`
}
// 格式化代码示例
export const formatCodeExample = (code, language = 'javascript') => {
if (typeof code !== 'string') return ''
return code.trim()
}
// 生成流程步骤类名
export const getStepClasses = (index, currentIndex, totalSteps) => {
const classes = ['auth-step']
if (index < currentIndex) {
classes.push('auth-step-completed')
} else if (index === currentIndex) {
classes.push('auth-step-active')
} else {
classes.push('auth-step-pending')
}
return classes.join(' ')
}
// 生成表格行类名
export const getTableRowClasses = (highlight = false, index = 0) => {
const classes = ['auth-table-row']
if (highlight) classes.push('auth-table-row-highlight')
if (index % 2 === 0) classes.push('auth-table-row-even')
return classes.join(' ')
}
// 生成图标容器类名
export const getIconContainerClasses = (size = 'medium', variant = 'default') => {
return `auth-icon-container auth-icon-container-${size} auth-icon-container-${variant}`
}
// 生成输入框类名
export const getInputClasses = (state = 'default', size = 'medium') => {
const classes = ['auth-input']
if (state !== 'default') {
classes.push(`auth-input-${state}`)
}
if (size !== 'medium') {
classes.push(`auth-input-${size}`)
}
return classes.join(' ')
}
// 生成通知/提示框类名
export const getAlertClasses = (type = 'info', dismissible = false) => {
const classes = ['auth-alert', `auth-alert-${type}`]
if (dismissible) {
classes.push('auth-alert-dismissible')
}
return classes.join(' ')
}
// 生成标签类名
export const getTagClasses = (variant = 'default', size = 'medium') => {
return `auth-tag auth-tag-${variant} auth-tag-${size}`
}
// 生成加载器类名
export const getSpinnerClasses = (size = 'medium') => {
return `auth-spinner auth-spinner-${size}`
}
// 生成下拉菜单类名
export const getDropdownClasses = (isOpen = false, direction = 'down') => {
const classes = ['auth-dropdown']
if (isOpen) classes.push('auth-dropdown-open')
classes.push(`auth-dropdown-${direction}`)
return classes.join(' ')
}
@@ -0,0 +1,233 @@
// auth-design 公共组合式函数
import { ref, computed, onMounted, onUnmounted } from 'vue'
/**
* 延迟函数
* @param {number} ms - 延迟毫秒数
* @returns {Promise<void>}
*/
export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
/**
* 防抖函数
* @param {Function} fn - 要防抖的函数
* @param {number} wait - 等待时间(毫秒)
* @returns {Function}
*/
export const useDebounce = (fn, wait = 300) => {
let timeout = null
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(() => fn(...args), wait)
}
}
/**
* 步骤流程管理
* @param {Array} steps - 步骤数组
* @param {number} stepDelay - 每步延迟时间
* @returns {Object}
*/
export const useStepFlow = (steps, stepDelay = 800) => {
const currentStep = ref(0)
const isProcessing = ref(false)
const startFlow = async () => {
isProcessing.value = true
for (let i = 0; i < steps.length; i++) {
currentStep.value = i + 1
await delay(stepDelay)
}
isProcessing.value = false
}
const resetFlow = () => {
currentStep.value = 0
isProcessing.value = false
}
return {
currentStep,
isProcessing,
startFlow,
resetFlow
}
}
/**
* 异步操作状态管理
* @returns {Object}
*/
export const useAsyncState = () => {
const isLoading = ref(false)
const error = ref(null)
const data = ref(null)
const execute = async (fn) => {
isLoading.value = true
error.value = null
try {
const result = await fn()
data.value = result
return result
} catch (err) {
error.value = err
throw err
} finally {
isLoading.value = false
}
}
const reset = () => {
isLoading.value = false
error.value = null
data.value = null
}
return {
isLoading,
error,
data,
execute,
reset
}
}
/**
* 定时器管理
* @returns {Object}
*/
export const useTimer = () => {
const timer = ref(null)
const isRunning = ref(false)
const start = (callback, interval) => {
if (timer.value) clearInterval(timer.value)
isRunning.value = true
timer.value = setInterval(callback, interval)
}
const stop = () => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
isRunning.value = false
}
}
onUnmounted(() => {
stop()
})
return {
isRunning,
start,
stop
}
}
/**
* 切换状态
* @param {boolean} initialValue - 初始值
* @returns {Object}
*/
export const useToggle = (initialValue = false) => {
const value = ref(initialValue)
const toggle = () => {
value.value = !value.value
}
const setTrue = () => {
value.value = true
}
const setFalse = () => {
value.value = false
}
return {
value,
toggle,
setTrue,
setFalse
}
}
/**
* 动画控制
* @param {number} duration - 动画持续时间(毫秒)
* @returns {Object}
*/
export const useAnimation = (duration = 300) => {
const isAnimating = ref(false)
const animate = async (callback) => {
isAnimating.value = true
await callback()
await delay(duration)
isAnimating.value = false
}
return {
isAnimating,
animate
}
}
/**
* 生成随机 ID
* @param {string} prefix - 前缀
* @returns {string}
*/
export const generateId = (prefix = 'id') => {
return `${prefix}_${Math.random().toString(36).substring(2, 15)}`
}
/**
* 格式化时间戳
* @param {number} timestamp - 时间戳
* @returns {string}
*/
export const formatTimestamp = (timestamp) => {
const date = new Date(timestamp)
return date.toLocaleTimeString()
}
/**
* 深拷贝对象
* @param {*} obj - 要拷贝的对象
* @returns {*}
*/
export const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj.getTime())
if (obj instanceof Array) return obj.map((item) => deepClone(item))
if (obj instanceof Object) {
const clonedObj = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key])
}
}
return clonedObj
}
}
/**
* 批量更新状态
* @param {Object} stateRef - 状态引用
* @param {Object} updates - 更新内容
*/
export const batchUpdate = (stateRef, updates) => {
Object.assign(stateRef.value, updates)
}
/**
* 评分转换为星星
* @param {number} score - 评分 (1-5)
* @returns {string}
*/
export const scoreToStars = (score) => {
return '⭐'.repeat(Math.floor(score))
}
@@ -0,0 +1,144 @@
// auth-design 公共样式配置
export const commonStyles = {
// 容器样式
container: {
base: 'auth-demo-container',
classes: {
border: '1px solid var(--vp-c-divider)',
background: 'var(--vp-c-bg-soft)',
borderRadius: '12px',
padding: '1.5rem',
margin: '1.5rem 0',
fontFamily: 'var(--vp-font-family-base)'
}
},
// 标题样式
header: {
title: {
fontWeight: '700',
fontSize: '1.2rem',
marginBottom: '0.3rem',
background: 'linear-gradient(120deg, var(--vp-c-brand), #9c27b0)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent'
},
subtitle: {
color: 'var(--vp-c-text-2)',
fontSize: '0.9rem'
}
},
// 按钮样式
button: {
base: 'auth-demo-btn',
primary: 'auth-demo-btn-primary',
variants: {
primary: {
background: 'var(--vp-c-brand)',
color: 'white'
},
success: {
background: '#22c55e',
color: 'white'
},
danger: {
background: '#ef4444',
color: 'white'
},
secondary: {
background: '#64748b',
color: 'white'
}
}
},
// 卡片样式
card: {
base: 'auth-demo-card',
background: 'var(--vp-c-bg)',
border: '1px solid var(--vp-c-divider)',
borderRadius: '10px',
padding: '1.25rem'
},
// 代码块样式
codeBlock: {
background: '#1e293b',
color: '#e2e8f0',
fontFamily: "'Courier New', monospace",
fontSize: '0.8rem',
lineHeight: '1.6',
padding: '0.75rem',
borderRadius: '6px'
}
}
// 动画配置
export const animations = {
fadeIn: {
name: 'fadeIn',
css: `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
`
},
slideIn: {
name: 'slideIn',
css: `
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`
},
pulse: {
name: 'pulse',
css: `
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
`
},
bounce: {
name: 'bounce',
css: `
@keyframes bounce {
0%, 100% { transform: translateX(-50%) translateY(0); }
50% { transform: translateX(-50%) translateY(-5px); }
}
`
},
spin: {
name: 'spin',
css: `
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
`
}
}
// 颜色配置
export const colors = {
success: '#22c55e',
warning: '#f59e0b',
danger: '#ef4444',
info: '#3b82f6',
purple: '#8b5cf6'
}
// 响应式断点
export const breakpoints = {
mobile: '768px'
}