diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md
index 60b828f..747f7f5 100644
--- a/.trae/rules/project_rules.md
+++ b/.trae/rules/project_rules.md
@@ -1,34 +1,42 @@
-如果是前端项目,需要考虑以下几点:
-你是一名精通 Vue3(纯 JavaScript,不使用 TypeScript)的前端开发专家,专注于构建可适配移动端、桌面端、平板端的响应式页面并且开发的页面都会基于当前项目搭建的中英文框架支持中英文切换(页面中英文切换内容都是本地的,所以需要在项目中配置好对应的英文内容),和主题色切换请基于以下要求完成开发任务:
-技术栈规范
+如果前端项目,需要考虑以下几点:
+1.你是一名精通 Vue3(纯 JavaScript,不使用 TypeScript)的前端开发专家,专注于构建可适配移动端、桌面端、平板端的响应式页面并且开发的页面都会基于当前项目搭建的中英文框架支持中英文切换(页面中英文切换内容都是本地的,所以需要在项目中配置好对应的英文内容),和主题色切换请基于以下要求完成开发任务:
+2.技术栈规范
核心框架:使用 Vue3(Options API 或 Composition API 均可,优先推荐 Composition API 以提升代码复用性),禁止使用 TypeScript,所有逻辑用原生 JavaScript 实现。
-构建工具:基于 Vite 搭建项目,确保热更新效率和打包性能。
-样式解决方案:
+3.构建工具:基于 Vite 搭建项目,确保热更新效率和打包性能。
+4.样式解决方案:
优先使用 Scoped CSS + CSS 变量实现组件样式隔离与主题定制,避免全局样式污染。
响应式布局必须结合 Flexbox/Grid,并通过媒体查询(media queries)适配不同设备尺寸(参考断点:移动端 <768px,平板 768px-1024px,桌面端> 1024px)。
可选集成 Tailwind CSS(若使用需说明配置方案),或原生 CSS 实现
-状态管理:简单场景用 Vue3 内置的reactive/ref+provide/inject;复杂场景使用 Pinia(需说明 Store 设计逻辑,禁止使用 Vuex)。
-路由管理:使用 Vue Router 4,配置动态路由和嵌套路由,确保多端路由跳转体验一致(移动端可优化为底部导航,桌面端为顶部 / 侧边导航)。
-UI 组件:
+5.状态管理:简单场景用 Vue3 内置的reactive/ref+provide/inject;复杂场景使用 Pinia(需说明 Store 设计逻辑,禁止使用 Vuex)。
+6.路由管理:使用 Vue Router 4,配置动态路由和嵌套路由,确保多端路由跳转体验一致(移动端可优化为底部导航,桌面端为顶部 / 侧边导航)。
+7.UI 组件:
如需使用 UI 库,优先选择轻量型适配多端的框架(如 Vant 4 适配移动端,Element Plus 适配桌面端,需说明多端组件切换逻辑),如果使用到Element Plus的图标,需要查一下图标库有没有该图标。
自定义组件需实现自适应尺寸(字体、间距、宽高随屏幕尺寸动态调整),避免固定像素值导致的适配问题。
-交互优化:
+8.交互优化:
移动端需添加触摸反馈(如点击态、滑动动效),支持手势操作(如左右滑动切换页面)。
桌面端优化鼠标悬停效果、键盘导航支持。
平板端兼顾触摸与鼠标操作,优化横 / 竖屏切换体验。
多端适配核心要求
-布局适配:
+9.布局适配:
采用 “移动优先” 原则设计布局,通过媒体查询逐步扩展至平板和桌面端。
关键区域(如头部、内容区、底部)需在不同设备上重排:移动端单列布局,平板双列 / 混合布局,桌面端多列布局。
图片 / 视频使用object-fit和响应式 srcset,确保在不同分辨率下清晰显示且不拉伸。
-性能优化:
+10.性能优化:
实现组件懒加载(基于defineAsyncComponent)和路由懒加载,减少首屏加载时间。
移动端禁止不必要的动画和重绘,确保 60fps 流畅度;桌面端可适当增加过渡效果提升体验。
监听窗口尺寸变化(resize事件),动态调整组件状态(避免频繁触发,需添加防抖处理)。
-兼容性:
+11.兼容性:
移动端兼容 iOS 13+、Android 8+;桌面端兼容 Chrome 80+、Firefox 75+、Edge 80+;平板端覆盖主流设备(iPadOS、Android 平板)。
避免使用 ES6 + 以上高级语法(或通过 Babel 转译),确保低版本浏览器兼容性。
-交付标准
+12.设计风格
+- 主色调:深紫色(#6B46C1)用于主要操作,浅紫色(#A78BFA)用于强调
+- 辅助色:深灰色(#1F2937)用于文本,浅灰色(#F3F4F6)用于背景
+- 按钮样式:圆角设计(8px半径),微妙阴影,悬停效果
+- 字体排版:Inter字体系列,16px基础大小,响应式缩放
+- 布局风格:基于卡片的设计,统一间距(8px网格系统)
+- 图标风格:Feather图标库,UI元素统一24px大小
+- 动画效果:平滑过渡(200ms缓入缓出),加载骨架屏
+13.交付标准
代码结构清晰,遵循 Vue3 最佳实践(如组件拆分粒度合理、逻辑与 UI 分离)。
提供完整的多端测试报告(说明在不同设备 / 尺寸下的测试结果及适配方案)。
附带 README,说明项目启动、打包命令,以及响应式布局的核心实现逻辑。
\ No newline at end of file
diff --git a/apps/FrontendDesigner/package.json b/apps/FrontendDesigner/package.json
index 838ed4e..3030347 100644
--- a/apps/FrontendDesigner/package.json
+++ b/apps/FrontendDesigner/package.json
@@ -4,25 +4,27 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite",
+ "dev": "vite --port 3001 --host",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
- "@deotaland/ui": "workspace:*",
- "@deotaland/utils": "workspace:*",
- "vue": "^3.5.24",
- "vue-router": "^4.4.5",
+ "@element-plus/icons-vue": "^2.3.2",
+ "element-plus": "^2.11.7",
"pinia": "^2.2.6",
- "vue-i18n": "^9.14.2"
+ "vue": "^3.5.24",
+ "vue-i18n": "^9.14.2",
+ "vue-router": "^4.4.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
- "vite": "^7.2.2",
"eslint": "^9.15.0",
"eslint-plugin-vue": "^9.32.0",
- "prettier": "^3.3.3"
+ "prettier": "^3.3.3",
+ "unplugin-auto-import": "^20.2.0",
+ "unplugin-vue-components": "^30.0.0",
+ "vite": "^7.2.2"
}
}
diff --git a/apps/FrontendDesigner/src/App.vue b/apps/FrontendDesigner/src/App.vue
index fa224c6..cf11d3d 100644
--- a/apps/FrontendDesigner/src/App.vue
+++ b/apps/FrontendDesigner/src/App.vue
@@ -56,6 +56,11 @@ const handleLanguageChange = (locale) => {
localStorage.setItem('preferred-language', locale)
}
+// 监听stores语言切换事件
+const handleStoreLocaleChange = (event) => {
+ // 已移除自定义事件,改用响应式监听
+}
+
// 主题切换处理
const handleThemeChange = () => {
toggleTheme()
@@ -101,10 +106,12 @@ const unwatchTheme = watch(() => currentTheme.value, () => {
onMounted(() => {
initializeApp()
window.addEventListener('resize', handleResize)
+ window.addEventListener('locale-changed', handleStoreLocaleChange)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
+ window.removeEventListener('locale-changed', handleStoreLocaleChange)
unwatchLocale()
unwatchTheme()
})
@@ -122,106 +129,6 @@ onUnmounted(() => {
'menu-open': showMobileMenu
}"
>
-
-
-
@@ -235,22 +142,6 @@ onUnmounted(() => {
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/composables/useI18n.js b/apps/FrontendDesigner/src/composables/useI18n.js
index 98f70c8..7a210aa 100644
--- a/apps/FrontendDesigner/src/composables/useI18n.js
+++ b/apps/FrontendDesigner/src/composables/useI18n.js
@@ -4,7 +4,7 @@
*/
import { ref, watch } from 'vue'
-import { useI18n } from 'vue-i18n'
+import { useI18n as useVueI18n } from 'vue-i18n'
import { useAppStore } from '../stores'
// 支持的语言列表
@@ -97,7 +97,7 @@ export function formatRelativeTime(date) {
}
export function useI18nExt() {
- const { locale: i18nLocale, messages, t, d } = useI18n()
+ const { locale: i18nLocale, messages, t, d } = useVueI18n()
const appStore = useAppStore()
// 当前语言
@@ -118,18 +118,15 @@ export function useI18nExt() {
// 更新当前语言状态
currentLocale.value = localeCode
- // 保存到 store 和 localStorage
+ // 只保存到 localStorage,store 由 App.vue 统一管理
if (saveToStorage) {
- appStore.setLanguage(localeCode)
+ localStorage.setItem('app-locale', localeCode)
}
// 更新 HTML lang 属性
document.documentElement.lang = localeCode
- // 触发语言变化事件
- window.dispatchEvent(new CustomEvent('locale-changed', {
- detail: { locale: localeCode }
- }))
+ // 不直接调用store,避免循环
// 更新日期格式
document.documentElement.dir = getTextDirection(localeCode)
@@ -213,7 +210,7 @@ export function useI18nExt() {
const savedLocale = localStorage.getItem('app-locale')
// 从 store 获取语言设置
- const storeLocale = appStore.language
+ const storeLocale = appStore.locale
// 优先级:store > localStorage > 浏览器检测
const initialLocale = storeLocale || savedLocale || detectBrowserLocale()
@@ -221,20 +218,8 @@ export function useI18nExt() {
// 设置初始语言
setLocale(initialLocale, false)
- // 监听 store 中的语言变化
- watch(() => appStore.language, (newLocale) => {
- if (newLocale !== currentLocale.value) {
- setLocale(newLocale, false)
- }
- })
-
- // 监听 i18n 内部语言变化
- watch(i18nLocale, (newLocale) => {
- if (newLocale !== currentLocale.value) {
- currentLocale.value = newLocale
- appStore.setLanguage(newLocale)
- }
- })
+ // 移除所有监听器,避免循环调用
+ // store 和 i18n 独立工作,避免相互干扰
}
return {
@@ -253,4 +238,44 @@ export function useI18nExt() {
formatRelativeTime,
initLocale
}
+}
+
+// 简化的导出供页面组件使用
+export function useI18n() {
+ const { locale: i18nLocale, t } = useVueI18n()
+ const appStore = useAppStore()
+
+ // 当前语言
+ const currentLocale = ref(i18nLocale.value)
+
+ // 设置语言
+ const setLocale = (localeCode, saveToStorage = true) => {
+ // 验证语言有效性
+ const isValidLocale = SUPPORTED_LOCALES.some(lang => lang.code === localeCode)
+ if (!isValidLocale) {
+ console.warn(`Invalid locale: ${localeCode}, falling back to 'en-US'`)
+ localeCode = 'en-US'
+ }
+
+ // 更新 i18n 语言
+ i18nLocale.value = localeCode
+
+ // 更新当前语言状态
+ currentLocale.value = localeCode
+
+ // 只保存到 localStorage,store 由 App.vue 统一管理
+ if (saveToStorage) {
+ localStorage.setItem('app-locale', localeCode)
+ }
+
+ // 更新 HTML lang 属性
+ document.documentElement.lang = localeCode
+ }
+
+ return {
+ currentLocale,
+ locales: SUPPORTED_LOCALES,
+ t,
+ setLocale
+ }
}
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/locales/lang/en-US.js b/apps/FrontendDesigner/src/locales/lang/en-US.js
index 6ab49cd..eceeb62 100644
--- a/apps/FrontendDesigner/src/locales/lang/en-US.js
+++ b/apps/FrontendDesigner/src/locales/lang/en-US.js
@@ -74,5 +74,206 @@ export default {
pageNotFound: 'Page Not Found',
goHome: 'Go Home',
description: 'Sorry, the page you are looking for does not exist.'
+ },
+
+ // Header navigation
+ header: {
+ toggleTheme: 'Toggle Theme',
+ switchLanguage: 'Switch Language'
+ },
+
+ // Messages
+ messages: {
+ profileComingSoon: 'Profile feature coming soon',
+ settingsComingSoon: 'Settings feature coming soon',
+ confirmLogout: 'Are you sure you want to logout?',
+ logout: 'Logout',
+ logoutSuccess: 'Logout successful'
+ },
+
+ admin: {
+ title: 'Admin Panel',
+ login: {
+ title: 'Admin Login',
+ username: 'Username',
+ password: 'Password',
+ rememberMe: 'Remember Me',
+ login: 'Login',
+ logging: 'Logging in...',
+ usernameRequired: 'Please enter username',
+ passwordRequired: 'Please enter password',
+ usernamePlaceholder: 'Please enter username',
+ passwordPlaceholder: 'Please enter password',
+ loginSuccess: 'Login successful',
+ loginFailed: 'Login failed, please check username and password',
+ welcome: 'Welcome to Admin Panel'
+ },
+ common: {
+ refresh: 'Refresh',
+ search: 'Search',
+ reset: 'Reset',
+ startDate: 'Start Date',
+ endDate: 'End Date',
+ loading: 'Loading...'
+ },
+ layout: {
+ dashboard: 'Dashboard',
+ content: 'Content Review',
+ contentReview: 'Content Review',
+ orders: 'Order Management',
+ users: 'User Management',
+ logout: 'Logout',
+ profile: 'Profile',
+ settings: 'Settings'
+ },
+ dashboard: {
+ title: 'Dashboard',
+ subtitle: 'System Overview and Key Metrics',
+ refresh: 'Refresh Data',
+ stats: {
+ totalUsers: 'Total Users',
+ totalRevenue: 'Total Revenue',
+ revenue: 'Revenue',
+ totalOrders: 'Total Orders',
+ growthRate: 'Growth Rate',
+ pendingReviews: 'Pending Reviews'
+ },
+ charts: {
+ salesTrend: 'Sales Trend',
+ orderStatus: 'Order Status Distribution',
+ recentActivity: 'Recent Activity'
+ },
+ activity: {
+ userRegistration: 'User Registration',
+ orderCreated: 'Order Created',
+ paymentReceived: 'Payment Received',
+ systemUpdate: 'System Update'
+ }
+ },
+ content: {
+ title: 'Content Management',
+ add: 'Add Content',
+ status: 'Status',
+ type: 'Type',
+ search: 'Search Content',
+ author: 'Author',
+ publishDate: 'Publish Date',
+ views: 'Views',
+ actions: 'Actions',
+ view: 'View',
+ edit: 'Edit',
+ delete: 'Delete',
+ statusOptions: {
+ published: 'Published',
+ pending: 'Pending',
+ draft: 'Draft',
+ rejected: 'Rejected'
+ },
+ typeOptions: {
+ article: 'Article',
+ image: 'Image',
+ video: 'Video'
+ }
+ },
+ orders: {
+ title: 'Order Management',
+ export: 'Export Orders',
+ search: 'Search Orders',
+ status: 'Status',
+ dateRange: 'Date Range',
+ orderNumber: 'Order Number',
+ customer: 'Customer',
+ total: 'Total Amount',
+ payment: 'Payment Method',
+ date: 'Order Date',
+ actions: 'Actions',
+ view: 'View',
+ updateStatus: 'Update Status',
+ detail: 'Order Detail',
+ basicInfo: 'Basic Information',
+ items: 'Order Items',
+ itemName: 'Item Name',
+ quantity: 'Quantity',
+ price: 'Price',
+ currentStatus: 'Current Status',
+ newStatus: 'New Status',
+ selectStatus: 'Select Status',
+ stats: {
+ total: 'Total Orders',
+ pending: 'Pending',
+ completed: 'Completed',
+ revenue: 'Total Revenue'
+ },
+ statusOptions: {
+ pending: 'Pending',
+ processing: 'Processing',
+ completed: 'Completed',
+ shipped: 'Shipped',
+ delivered: 'Delivered',
+ cancelled: 'Cancelled'
+ },
+ paymentOptions: {
+ alipay: 'Alipay',
+ wechat: 'WeChat Pay',
+ credit: 'Credit Card'
+ }
+ },
+ users: {
+ title: 'User Management',
+ add: 'Add User',
+ search: 'Search Users',
+ status: 'Status',
+ role: 'Role',
+ registerDate: 'Register Date',
+ username: 'Username',
+ email: 'Email',
+ phone: 'Phone',
+ lastLogin: 'Last Login',
+ loginCount: 'Login Count',
+ actions: 'Actions',
+ view: 'View',
+ edit: 'Edit',
+ resetPassword: 'Reset Password',
+ ban: 'Ban',
+ unban: 'Unban',
+ detail: 'User Detail',
+ selectRole: 'Select Role',
+ selectStatus: 'Select Status',
+ save: 'Save',
+ stats: {
+ total: 'Total Users',
+ active: 'Active Users',
+ inactive: 'Inactive Users',
+ vip: 'VIP Users'
+ },
+ statusOptions: {
+ active: 'Active',
+ inactive: 'Inactive',
+ banned: 'Banned'
+ },
+ roleOptions: {
+ admin: 'Administrator',
+ user: 'Regular User',
+ vip: 'VIP User'
+ }
+ }
+ },
+
+ // 通用文本
+ common: {
+ confirm: 'Confirm',
+ cancel: 'Cancel',
+ save: 'Save',
+ delete: 'Delete',
+ edit: 'Edit',
+ search: 'Search',
+ loading: 'Loading...',
+ viewAll: 'View All',
+ reset: 'Reset',
+ noData: 'No Data',
+ error: 'Error',
+ success: 'Success',
+ warning: 'Warning',
+ info: 'Info'
}
}
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/locales/lang/zh-CN.js b/apps/FrontendDesigner/src/locales/lang/zh-CN.js
index 17e91f7..e9397d9 100644
--- a/apps/FrontendDesigner/src/locales/lang/zh-CN.js
+++ b/apps/FrontendDesigner/src/locales/lang/zh-CN.js
@@ -74,5 +74,226 @@ export default {
pageNotFound: '页面不存在',
goHome: '返回首页',
description: '抱歉,您访问的页面不存在。'
+ },
+
+ // 头部导航
+ header: {
+ toggleTheme: '切换主题',
+ switchLanguage: '切换语言'
+ },
+
+ // 消息提示
+ messages: {
+ profileComingSoon: '个人资料功能即将推出',
+ settingsComingSoon: '设置功能即将推出',
+ confirmLogout: '确定要退出登录吗?',
+ logout: '退出登录',
+ logoutSuccess: '退出成功'
+ },
+
+ // 管理后台
+ admin: {
+ title: '管理后台',
+ login: {
+ title: '管理员登录',
+ username: '用户名',
+ password: '密码',
+ rememberMe: '记住我',
+ login: '登录',
+ logging: '登录中...',
+ usernameRequired: '请输入用户名',
+ passwordRequired: '请输入密码',
+ usernamePlaceholder: '请输入用户名',
+ passwordPlaceholder: '请输入密码',
+ loginSuccess: '登录成功',
+ loginFailed: '登录失败,请检查用户名和密码',
+ welcome: '欢迎使用管理后台'
+ },
+ dashboard: {
+ title: '仪表板',
+ subtitle: '系统概览和关键指标',
+ refresh: '刷新数据',
+ stats: {
+ totalUsers: '总用户数',
+ totalRevenue: '总收入',
+ revenue: '收入',
+ totalOrders: '总订单数',
+ growthRate: '增长率',
+ pendingReviews: '待审核内容'
+ },
+ charts: {
+ salesTrend: '销售趋势',
+ orderStatus: '订单状态分布',
+ recentActivity: '最近活动'
+ },
+ activity: {
+ userRegistration: '用户注册',
+ orderCreated: '订单创建',
+ paymentReceived: '收到付款',
+ systemUpdate: '系统更新'
+ }
+ },
+ common: {
+ refresh: '刷新',
+ search: '搜索',
+ reset: '重置',
+ startDate: '开始日期',
+ endDate: '结束日期',
+ loading: '加载中...'
+ },
+ layout: {
+ dashboard: '仪表板',
+ content: '内容审核',
+ contentReview: '内容审核',
+ orders: '订单管理',
+ users: '用户管理',
+ logout: '退出登录',
+ profile: '个人资料',
+ settings: '设置',
+ notifications: '通知'
+ },
+ pages: {
+ content: {
+ title: '内容管理',
+ add: '添加内容',
+ status: '状态',
+ type: '类型',
+ search: '搜索内容',
+ author: '作者',
+ publishDate: '发布时间',
+ views: '浏览量',
+ actions: '操作',
+ view: '查看',
+ edit: '编辑',
+ delete: '删除',
+ statusOptions: {
+ published: '已发布',
+ pending: '待审核',
+ draft: '草稿',
+ rejected: '已拒绝'
+ },
+ typeOptions: {
+ article: '文章',
+ image: '图片',
+ video: '视频'
+ }
+ },
+ orders: {
+ title: '订单管理',
+ export: '导出订单',
+ search: '搜索订单',
+ status: '状态',
+ dateRange: '日期范围',
+ orderNumber: '订单号',
+ customer: '客户',
+ total: '总金额',
+ payment: '支付方式',
+ date: '下单日期',
+ actions: '操作',
+ view: '查看',
+ updateStatus: '更新状态',
+ detail: '订单详情',
+ basicInfo: '基本信息',
+ items: '订单商品',
+ itemName: '商品名称',
+ quantity: '数量',
+ price: '价格',
+ currentStatus: '当前状态',
+ newStatus: '新状态',
+ selectStatus: '选择状态',
+ stats: {
+ total: '总订单',
+ pending: '待处理',
+ completed: '已完成',
+ revenue: '总收入'
+ },
+ statusOptions: {
+ pending: '待处理',
+ processing: '处理中',
+ completed: '已完成',
+ shipped: '已发货',
+ delivered: '已送达',
+ cancelled: '已取消'
+ },
+ paymentOptions: {
+ alipay: '支付宝',
+ wechat: '微信支付',
+ credit: '信用卡'
+ }
+ },
+ users: {
+ title: '用户管理',
+ add: '添加用户',
+ search: '搜索用户',
+ status: '状态',
+ role: '角色',
+ registerDate: '注册日期',
+ username: '用户名',
+ email: '邮箱',
+ phone: '手机号',
+ lastLogin: '最后登录',
+ loginCount: '登录次数',
+ actions: '操作',
+ view: '查看',
+ edit: '编辑',
+ resetPassword: '重置密码',
+ ban: '封禁',
+ unban: '解封',
+ detail: '用户详情',
+ selectRole: '选择角色',
+ selectStatus: '选择状态',
+ save: '保存',
+ stats: {
+ total: '总用户',
+ active: '活跃用户',
+ inactive: '非活跃用户',
+ vip: 'VIP用户'
+ },
+ statusOptions: {
+ active: '活跃',
+ inactive: '非活跃',
+ banned: '已封禁'
+ },
+ roleOptions: {
+ admin: '管理员',
+ user: '普通用户',
+ vip: 'VIP用户'
+ }
+ }
+ }
+ },
+
+ // 头部导航
+ header: {
+ toggleTheme: '切换主题',
+ switchLanguage: '切换语言',
+ notifications: '通知'
+ },
+
+ // 消息提示
+ messages: {
+ profileComingSoon: '个人资料功能即将推出',
+ settingsComingSoon: '设置功能即将推出',
+ confirmLogout: '确定要退出登录吗?',
+ logout: '退出登录',
+ logoutSuccess: '退出登录成功'
+ },
+
+ // 通用文本
+ common: {
+ confirm: '确认',
+ cancel: '取消',
+ save: '保存',
+ delete: '删除',
+ edit: '编辑',
+ search: '搜索',
+ loading: '加载中...',
+ viewAll: '查看全部',
+ reset: '重置',
+ noData: '暂无数据',
+ error: '错误',
+ success: '成功',
+ warning: '警告',
+ info: '信息'
}
}
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/main.js b/apps/FrontendDesigner/src/main.js
index 4b5156b..068eabd 100644
--- a/apps/FrontendDesigner/src/main.js
+++ b/apps/FrontendDesigner/src/main.js
@@ -4,10 +4,15 @@ import router from './router'
import App from './App.vue'
// 导入样式
+import 'element-plus/dist/index.css'
+import 'element-plus/theme-chalk/dark/css-vars.css'
import './assets/styles/global.css'
import './assets/styles/responsive.css'
import './assets/styles/themes.css'
+// 导入Element Plus图标
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+
// 导入i18n配置
import i18n from './locales/i18n'
@@ -24,5 +29,10 @@ app.use(i18n)
// 配置路由
app.use(router)
+// 注册所有Element Plus图标
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+ app.component(key, component)
+}
+
// 挂载应用
app.mount('#app')
diff --git a/apps/FrontendDesigner/src/router/index.js b/apps/FrontendDesigner/src/router/index.js
index 648be1d..e7a29f9 100644
--- a/apps/FrontendDesigner/src/router/index.js
+++ b/apps/FrontendDesigner/src/router/index.js
@@ -1,17 +1,22 @@
import { createRouter, createWebHistory } from 'vue-router'
+import About from '@/views/About.vue'
+import NotFound from '@/views/NotFound.vue'
+import AdminLogin from '@/views/AdminLogin.vue'
-// 懒加载路由组件
-const Home = () => import('../views/Home.vue')
-const About = () => import('../views/About.vue')
-const NotFound = () => import('../views/NotFound.vue')
+// 管理员布局组件(懒加载)
+const AdminLayout = () => import('@/components/admin/AdminLayout.vue')
+const AdminDashboard = () => import('@/views/admin/AdminDashboard.vue')
+const AdminContent = () => import('@/views/admin/AdminContent.vue')
+const AdminOrders = () => import('@/views/admin/AdminOrders.vue')
+const AdminUsers = () => import('@/views/admin/AdminUsers.vue')
const routes = [
{
path: '/',
name: 'Home',
- component: Home,
+ redirect: '/admin/login',
meta: {
- title: '首页'
+ title: '首页重定向'
}
},
{
@@ -19,15 +24,70 @@ const routes = [
name: 'About',
component: About,
meta: {
- title: '关于'
+ title: '关于页面'
}
},
+ {
+ path: '/admin/login',
+ name: 'AdminLogin',
+ component: AdminLogin,
+ meta: {
+ title: '管理员登录',
+ requiresAuth: false
+ }
+ },
+ {
+ path: '/admin',
+ component: AdminLayout,
+ meta: {
+ requiresAuth: true
+ },
+ children: [
+ {
+ path: '',
+ name: 'AdminDashboard',
+ component: AdminDashboard,
+ meta: {
+ title: '仪表板'
+ }
+ },
+ {
+ path: 'dashboard',
+ name: 'AdminDashboardAlias',
+ redirect: '/admin'
+ },
+ {
+ path: 'content',
+ name: 'AdminContent',
+ component: AdminContent,
+ meta: {
+ title: '内容管理'
+ }
+ },
+ {
+ path: 'orders',
+ name: 'AdminOrders',
+ component: AdminOrders,
+ meta: {
+ title: '订单管理'
+ }
+ },
+ {
+ path: 'users',
+ name: 'AdminUsers',
+ component: AdminUsers,
+ meta: {
+ title: '用户管理'
+ }
+ }
+ ]
+ },
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
meta: {
- title: '404页面'
+ title: '页面不存在'
}
}
]
@@ -44,11 +104,31 @@ const router = createRouter({
}
})
-// 路由守卫 - 设置页面标题
+// 路由守卫 - 认证检查
router.beforeEach((to, from, next) => {
- if (to.meta && to.meta.title) {
- document.title = `${to.meta.title} - FrontendDesigner`
+ // 设置页面标题
+ if (to.meta?.title) {
+ document.title = to.meta.title
}
+
+ // 检查是否需要认证
+ if (to.meta?.requiresAuth) {
+ const token = localStorage.getItem('admin-token')
+ if (!token) {
+ next('/admin/login')
+ return
+ }
+ }
+
+ // 如果已登录且访问登录页面,重定向到管理后台首页
+ if (to.path === '/admin/login') {
+ const token = localStorage.getItem('admin-token')
+ if (token) {
+ next('/admin')
+ return
+ }
+ }
+
next()
})
diff --git a/apps/FrontendDesigner/src/stores/index.js b/apps/FrontendDesigner/src/stores/index.js
index 6fc0d30..b82e465 100644
--- a/apps/FrontendDesigner/src/stores/index.js
+++ b/apps/FrontendDesigner/src/stores/index.js
@@ -1,5 +1,77 @@
import { defineStore } from 'pinia'
+// 管理员认证状态管理store
+export const useAuthStore = defineStore('auth', {
+ state: () => ({
+ token: localStorage.getItem('admin-token') || '',
+ user: JSON.parse(localStorage.getItem('admin-user') || 'null')
+ }),
+
+ getters: {
+ // 判断是否已登录
+ isAuthenticated: (state) => !!state.token && !!state.user,
+ // 获取用户名
+ username: (state) => state.user?.username || '',
+ // 获取用户角色
+ userRole: (state) => state.user?.role || ''
+ },
+
+ actions: {
+ // 登录
+ login(loginData) {
+ const { token, user } = loginData
+
+ this.token = token
+ this.user = user
+
+ // 持久化存储
+ localStorage.setItem('admin-token', token)
+ localStorage.setItem('admin-user', JSON.stringify(user))
+ },
+
+ // 登出
+ logout() {
+ this.token = ''
+ this.user = null
+
+ // 清除持久化存储
+ localStorage.removeItem('admin-token')
+ localStorage.removeItem('admin-user')
+ localStorage.removeItem('admin-remember-me')
+ },
+
+ // 检查认证状态
+ checkAuth() {
+ const storedToken = localStorage.getItem('admin-token')
+ const storedUser = localStorage.getItem('admin-user')
+
+ if (storedToken && storedUser) {
+ this.token = storedToken
+ this.user = JSON.parse(storedUser)
+ return true
+ }
+
+ return false
+ },
+
+ // 检查权限
+ hasPermission(requiredRole) {
+ if (!this.user) return false
+
+ const roleHierarchy = {
+ 'admin': 3,
+ 'manager': 2,
+ 'user': 1
+ }
+
+ const userLevel = roleHierarchy[this.user.role] || 0
+ const requiredLevel = roleHierarchy[requiredRole] || 0
+
+ return userLevel >= requiredLevel
+ }
+ }
+})
+
// 全局状态管理store
export const useAppStore = defineStore('app', {
state: () => ({
@@ -49,12 +121,14 @@ export const useAppStore = defineStore('app', {
toggleLocale() {
this.locale = this.locale === 'zh-CN' ? 'en-US' : 'zh-CN'
localStorage.setItem('locale', this.locale)
+ // 不再触发事件,由Vue的响应式系统自动通知
},
// 设置语言
setLocale(locale) {
this.locale = locale
localStorage.setItem('locale', locale)
+ // 不再触发事件,由Vue的响应式系统自动通知
},
// 设置加载状态
diff --git a/apps/FrontendDesigner/src/views/AdminLogin.vue b/apps/FrontendDesigner/src/views/AdminLogin.vue
new file mode 100644
index 0000000..44cb394
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/AdminLogin.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('admin.login.rememberMe') }}
+
+
+
+
+
+
+ {{ loading ? t('admin.login.logging') : t('admin.login.login') }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/views/Home.vue b/apps/FrontendDesigner/src/views/Home.vue
index aaf2e21..732f9ad 100644
--- a/apps/FrontendDesigner/src/views/Home.vue
+++ b/apps/FrontendDesigner/src/views/Home.vue
@@ -1,10 +1,10 @@
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/views/admin/AdminDashboard.vue b/apps/FrontendDesigner/src/views/admin/AdminDashboard.vue
new file mode 100644
index 0000000..ddbdc2f
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/admin/AdminDashboard.vue
@@ -0,0 +1,621 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ stats.totalUsers }}
+
{{ t('admin.dashboard.stats.totalUsers') }}
+
+
+
+
+
+
+
+
+
+
+
{{ stats.totalOrders }}
+
{{ t('admin.dashboard.stats.totalOrders') }}
+
+
+
+
+
+
+
+
+
+
+
{{ stats.pendingReviews }}
+
{{ t('admin.dashboard.stats.pendingReviews') }}
+
+
+
+
+
+
+
+
+
+
+
¥{{ formatNumber(stats.revenue) }}
+
{{ t('admin.dashboard.stats.revenue') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ point.date }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('admin.orders.statusOptions.completed') }} ({{ orderStats.completed }}%)
+
+
+
+
{{ t('admin.orders.statusOptions.pending') }} ({{ orderStats.pending }}%)
+
+
+
+
{{ t('admin.orders.statusOptions.cancelled') }} ({{ orderStats.cancelled }}%)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ activity.title }}
+
{{ formatTime(activity.time) }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/views/admin/AdminOrders.vue b/apps/FrontendDesigner/src/views/admin/AdminOrders.vue
new file mode 100644
index 0000000..dd9de0e
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/admin/AdminOrders.vue
@@ -0,0 +1,664 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ orderStats.total }}
+
{{ t('admin.orders.stats.total') }}
+
+
+
+
+
+
+
+
+
{{ orderStats.pending }}
+
{{ t('admin.orders.stats.pending') }}
+
+
+
+
+
+
+
+
+
{{ orderStats.completed }}
+
{{ t('admin.orders.stats.completed') }}
+
+
+
+
+
+
+
+
+
¥{{ orderStats.revenue.toLocaleString() }}
+
{{ t('admin.orders.stats.revenue') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ¥{{ row.totalAmount.toFixed(2) }}
+
+
+
+
+
+ {{ t(`admin.orders.status.${row.status}`) }}
+
+
+
+
+
+ {{ t(`admin.orders.payment.${row.paymentMethod}`) }}
+
+
+
+
+ {{ formatDate(row.orderDate) }}
+
+
+
+
+
+ {{ t('admin.orders.view') }}
+
+
+ {{ t('admin.orders.updateStatus') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('admin.orders.basicInfo') }}
+
+
+ {{ selectedOrder.orderNumber }}
+
+
+ {{ selectedOrder.customerName }}
+
+
+ ¥{{ selectedOrder.totalAmount.toFixed(2) }}
+
+
+
+ {{ t(`admin.orders.status.${selectedOrder.status}`) }}
+
+
+
+
+
+
+
{{ t('admin.orders.items') }}
+
+
+
+
+
+ ¥{{ row.price.toFixed(2) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t(`admin.orders.status.${selectedOrderForStatus.status}`) }}
+
+
+
+
+
+
+
+
+
+ {{ t('common.cancel') }}
+ {{ t('common.confirm') }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/views/admin/AdminUsers.vue b/apps/FrontendDesigner/src/views/admin/AdminUsers.vue
new file mode 100644
index 0000000..96565d1
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/admin/AdminUsers.vue
@@ -0,0 +1,689 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ userStats.total }}
+
{{ $t('admin.users.stats.total') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ userStats.active }}
+
{{ $t('admin.users.stats.active') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ userStats.inactive }}
+
{{ $t('admin.users.stats.inactive') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ userStats.vip }}
+
{{ $t('admin.users.stats.vip') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('admin.common.search') }}
+
+ {{ $t('admin.common.reset') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(`admin.users.roleOptions.${row.role}`) }}
+
+
+
+
+
+
+ {{ $t(`admin.users.statusOptions.${row.status}`) }}
+
+
+
+
+
+
+
+
+ {{ $t('admin.users.view') }}
+
+
+ {{ $t('admin.users.edit') }}
+
+
+ {{ $t('admin.users.resetPassword') }}
+
+
+ {{ $t('admin.users.ban') }}
+
+
+ {{ $t('admin.users.unban') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ selectedUser?.username }}
+
+
+ {{ selectedUser?.email }}
+
+
+ {{ selectedUser?.phone }}
+
+
+ {{ selectedUser ? $t(`admin.users.roleOptions.${selectedUser.role}`) : '' }}
+
+
+ {{ selectedUser ? $t(`admin.users.statusOptions.${selectedUser.status}`) : '' }}
+
+
+ {{ selectedUser?.loginCount }}
+
+
+ {{ selectedUser?.registerDate }}
+
+
+ {{ selectedUser?.lastLogin }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/vite.config.js b/apps/FrontendDesigner/vite.config.js
index bbcf80c..8d8f2f9 100644
--- a/apps/FrontendDesigner/vite.config.js
+++ b/apps/FrontendDesigner/vite.config.js
@@ -1,7 +1,24 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import { resolve } from 'path'
// https://vite.dev/config/
export default defineConfig({
- plugins: [vue()],
+ plugins: [
+ vue(),
+ AutoImport({
+ resolvers: [ElementPlusResolver()],
+ }),
+ Components({
+ resolvers: [ElementPlusResolver()],
+ }),
+ ],
+ resolve: {
+ alias: {
+ '@': resolve(__dirname, './src'),
+ },
+ },
})
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 0a856f1..20e0eeb 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite",
+ "dev": "vite --port 3000 --host",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src",
diff --git a/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue b/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
index afa779c..d2e143c 100644
--- a/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
+++ b/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
@@ -55,7 +55,6 @@
{
console.log('会员按钮点击')
}
-// 处理语言切换
-const handleLanguageChange = (lang) => {
- console.log('语言切换到:', lang)
- // 语言切换已由LanguageToggle组件内部处理
-}
+// 语言切换功能已由LanguageToggle组件内部处理
// 处理返回按钮点击
const handleBack = () => {
diff --git a/apps/frontend/src/components/ui/LanguageToggle.vue b/apps/frontend/src/components/ui/LanguageToggle.vue
index a247196..6cc43b8 100644
--- a/apps/frontend/src/components/ui/LanguageToggle.vue
+++ b/apps/frontend/src/components/ui/LanguageToggle.vue
@@ -13,16 +13,9 @@ const props = defineProps({
tooltipText: {
type: String,
default: ''
- },
- // 语言切换回调函数
- onLanguageChange: {
- type: Function,
- default: null
}
})
-const emit = defineEmits(['language-change'])
-
const { locale } = useI18n()
// 支持的语言列表
@@ -51,14 +44,6 @@ function toggleLanguage() {
// 保存到localStorage
localStorage.setItem('lang', nextLang.code)
-
- // 触发事件
- emit('language-change', nextLang.code)
-
- // 调用外部回调函数
- if (props.onLanguageChange) {
- props.onLanguageChange(nextLang.code)
- }
}
diff --git a/apps/frontend/src/views/ForgotPassword.vue b/apps/frontend/src/views/ForgotPassword.vue
index a183cac..aa1c310 100644
--- a/apps/frontend/src/views/ForgotPassword.vue
+++ b/apps/frontend/src/views/ForgotPassword.vue
@@ -13,7 +13,6 @@
@@ -115,10 +114,7 @@ const goToRegister = () => {
router.push('/register')
}
-// 处理语言切换
-const handleLanguageChange = (language) => {
- console.log('语言切换到:', language)
-}
+
// 页面挂载时初始化认证状态
onMounted(() => {
diff --git a/apps/frontend/src/views/Login.vue b/apps/frontend/src/views/Login.vue
index a786d5e..e922b68 100644
--- a/apps/frontend/src/views/Login.vue
+++ b/apps/frontend/src/views/Login.vue
@@ -13,7 +13,6 @@