diff --git a/.trae/documents/优化IPCard组件生成状态样式.md b/.trae/documents/优化IPCard组件生成状态样式.md
index 8a04102..30e2e43 100644
--- a/.trae/documents/优化IPCard组件生成状态样式.md
+++ b/.trae/documents/优化IPCard组件生成状态样式.md
@@ -1,203 +1,201 @@
-# 优化IPCard组件生成状态样式
+# 新建项目系列选择功能实现
-## 现状分析
+## 需求分析
-当前IPCard组件在图片生成过程中显示的占位符(.generating-placeholder)样式较为简单,主要包含:
+* 当用户点击"新建项目"卡片时,需要弹出系列选择弹窗
-* 深灰色背景(#2a2a2a)
+* 系列弹窗包含两个选项:Done 和 Oone,对应图片在 src/assets/xh 文件夹中
-* 基础的圆形旋转加载动画
+* 选中后将系列名称作为 type 参数传递给 createNewProject 函数
-* 静态文本"正在生成图片..."
+## 实现计划
-## 优化目标
+### 1. 创建 SeriesSelector 组件
-使生成状态更加灵动、美观,符合项目的设计风格(深紫色主色调),提升用户体验。
+* **文件路径**:`src/views/components/SeriesSelector.vue`
-## 优化方案
+* **功能**:
-### 1. 背景效果优化
+ * 显示两个系列选项(Done 和 Oone)
-* 将纯色背景改为渐变背景,与主题色呼应
+ * 每个选项显示对应的图片
-* 添加微妙的呼吸动画效果
+ * 支持选中状态切换
-### 2. 加载动画优化
+ * 提供确认和取消按钮
-* 设计双层旋转动画,增强视觉层次感
+ * 通过 emit 事件返回选中的系列名称
-* 使用主题色(#A78BFA)作为动画主色调
+### 2. 修改 CreationWorkspace.vue
-* 添加脉冲效果,使动画更加生动
+* **引入组件**:在 CreationWorkspace.vue 中引入 SeriesSelector 组件
-### 3. 文本效果优化
+* **添加状态管理**:
-* 添加渐变文字效果
+ * `showSeriesSelector`:控制系列选择弹窗的显示/隐藏
-* 保持与主题色的一致性
+* **修改新建项目逻辑**:
-### 4. 整体布局优化
+ * 点击"新建项目"卡片时,显示系列选择弹窗
-* 添加卡片阴影和立体感
+ * 监听 SeriesSelector 的确认事件,获取选中的系列名称
-* 确保动画流畅,性能优化
+ * 将系列名称作为 type 参数调用 createNewProject 函数
-## 具体实现
+### 3. 样式设计
-### 修改样式代码
+* 系列选择弹窗采用与现有删除确认弹窗一致的设计风格
-1. **更新.generating-placeholder样式**:
+* 系列选项卡片包含图片和名称,支持悬停和选中效果
- * 添加渐变背景
+* 确认和取消按钮使用现有按钮样式
- * 添加呼吸动画
+## 代码结构
- * 优化文本样式
+### SeriesSelector.vue
-2. **更新.generating-spinner样式**:
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
Done
+
+
+
+
+
+

+
+
Oone
+
+
+
+
+
+
+
+
+
+
+
+
+
- * 设计双层旋转结构
+
-## 预期效果
+
```
-这个优化方案将使IPCard
diff --git a/.trae/documents/实现3MF格式模型导出功能.md b/.trae/documents/实现3MF格式模型导出功能.md
new file mode 100644
index 0000000..c1d0bf3
--- /dev/null
+++ b/.trae/documents/实现3MF格式模型导出功能.md
@@ -0,0 +1,36 @@
+# 实现3MF格式模型导出功能
+
+## 1. 安装依赖
+- 安装 `three-3mf-exporter` 插件库,用于支持3MF格式导出
+
+## 2. 修改 ModelViewer.vue 组件
+### 2.1 导入3MF导出器
+在组件的脚本部分添加3MF导出器的导入
+
+### 2.2 更新导出选项
+在导出下拉菜单中添加3MF格式选项
+
+### 2.3 实现3MF导出功能
+添加 `exportAs3MF` 方法,使用3MF导出器将模型导出为3MF格式
+
+### 2.4 更新导出命令处理
+在 `handleExportCommand` 方法中添加对3MF格式的支持
+
+### 2.5 添加国际化支持
+在国际化文件中添加3MF相关的文本翻译
+
+## 3. 测试功能
+- 运行开发服务器,测试3MF导出功能是否正常工作
+- 检查导出的3MF文件是否可以正常打开
+
+## 4. 兼容性检查
+- 确保3MF导出功能与现有导出功能兼容
+- 确保3MF导出功能在不同设备上都能正常工作
+
+## 5. 代码优化
+- 确保代码符合项目的代码风格和最佳实践
+- 优化导出性能,确保导出过程流畅
+
+## 6. 文档更新
+- 更新组件文档,说明3MF导出功能的使用方法
+- 更新项目文档,添加3MF格式支持的说明
\ No newline at end of file
diff --git a/apps/FrontendDesigner/package.json b/apps/FrontendDesigner/package.json
index 20b7bf2..4186c47 100644
--- a/apps/FrontendDesigner/package.json
+++ b/apps/FrontendDesigner/package.json
@@ -20,6 +20,7 @@
"konva": "^10.0.12",
"pinia": "^2.2.6",
"three": "^0.180.0",
+ "three-3mf-exporter": "^45.0.0",
"vue": "^3.5.24",
"vue-i18n": "^9.14.2",
"vue-konva": "^3.2.6",
diff --git a/apps/FrontendDesigner/src/components/admin/SubMenu.vue b/apps/FrontendDesigner/src/components/admin/SubMenu.vue
index 179b28e..6d0a322 100644
--- a/apps/FrontendDesigner/src/components/admin/SubMenu.vue
+++ b/apps/FrontendDesigner/src/components/admin/SubMenu.vue
@@ -35,7 +35,8 @@ import {
Lock,
Key,
List,
- Coin
+ Coin,
+ ShoppingCartFull
} from '@element-plus/icons-vue'
const props = defineProps({
@@ -60,7 +61,8 @@ const iconMap = {
Lock,
Key,
List,
- Coin
+ Coin,
+ ShoppingCartFull
}
const getIconComponent = (iconName) => {
diff --git a/apps/FrontendDesigner/src/components/common/ModelViewer.vue b/apps/FrontendDesigner/src/components/common/ModelViewer.vue
index 4609fab..048a47b 100644
--- a/apps/FrontendDesigner/src/components/common/ModelViewer.vue
+++ b/apps/FrontendDesigner/src/components/common/ModelViewer.vue
@@ -91,6 +91,10 @@
●
{{ t('modelViewer.exportAsFBX') }}
+
+ ●
+ {{ t('modelViewer.exportAs3MF') }}
+
@@ -116,7 +120,6 @@ import { OBJExporter } from 'three/examples/jsm/exporters/OBJExporter.js'
import { STLExporter } from 'three/examples/jsm/exporters/STLExporter.js'
import { ElMessage } from 'element-plus'
import { Loading, Warning, Refresh, Grid, Position, Download, ArrowDown } from '@element-plus/icons-vue'
-
const { t } = useI18n()
// Props
@@ -445,6 +448,9 @@ const handleExportCommand = (command) => {
case 'fbx':
exportAsFBX()
break
+ case '3mf':
+ exportAs3MF()
+ break
default:
ElMessage.error('不支持的导出格式')
break
@@ -644,6 +650,57 @@ const exportAsFBX = () => {
}
}
+// 导出为3MF格式
+const exportAs3MF = async () => {
+ if (!model || !scene) {
+ ElMessage({
+ type: 'warning',
+ message: 'No model to export'
+ })
+ return
+ }
+
+ isExporting.value = true
+
+ ElMessage({
+ type: 'info',
+ message: t('modelViewer.exportInProgress'),
+ duration: 2000
+ })
+
+ try {
+ // 动态导入 three-3mf-exporter
+ const { exportTo3MF } = await import('three-3mf-exporter')
+
+ // 直接调用 exportTo3MF 函数,无需创建实例
+ const blob = await exportTo3MF(model, {
+ compression: 'standard',
+ })
+
+ const url = URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `${modelInfo.value || 'model'}.3mf`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+
+ ElMessage({
+ type: 'success',
+ message: t('modelViewer.exportSuccess')
+ })
+ } catch (error) {
+ console.error('3MF export error:', error)
+ ElMessage({
+ type: 'error',
+ message: t('modelViewer.exportFailed')
+ })
+ } finally {
+ isExporting.value = false
+ }
+}
+
// 鼠标事件处理(保留缩放功能)
const handleMouseDown = () => {
// 鼠标事件由 OrbitControls 处理
diff --git a/apps/FrontendDesigner/src/locales/lang/en-US.js b/apps/FrontendDesigner/src/locales/lang/en-US.js
index 150afc3..b4e0c04 100644
--- a/apps/FrontendDesigner/src/locales/lang/en-US.js
+++ b/apps/FrontendDesigner/src/locales/lang/en-US.js
@@ -40,6 +40,7 @@ export default {
exportAsOBJ: 'Export as OBJ',
exportAsSTL: 'Export as STL',
exportAsFBX: 'Export as FBX',
+ exportAs3MF: 'Export as 3MF',
selectFormat: 'Select Export Format',
preparingExport: 'Preparing export...'
},
@@ -243,6 +244,7 @@ export default {
yes: 'Yes',
no: 'No',
action: 'Action',
+ actions: 'Actions',
close: 'Close',
back: 'Back'
},
@@ -260,6 +262,8 @@ export default {
userList: 'User List',
pointsManagement: 'Points Management',
commissionManagement: 'Commission Management',
+ promptManagement: 'Prompt Management',
+ productManagement: 'Product Management',
logout: 'Logout',
profile: 'Profile',
settings: 'Settings'
@@ -780,6 +784,76 @@ export default {
approved: 'Approved',
rejected: 'Rejected'
}
+ },
+ promptManagement: {
+ title: 'Prompt Management',
+ addPrompt: 'Add Prompt',
+ editPrompt: 'Edit Prompt',
+ promptDetail: 'Prompt Detail',
+ inactivePrompts: 'Inactive Prompts',
+ activePrompts: 'Active Prompts',
+ type: 'Type',
+ selectType: 'Select Type',
+ animal: 'Animal',
+ person: 'Person',
+ general: 'General',
+ O1: 'O1',
+ title: 'Title',
+ enterTitle: 'Please enter prompt title',
+ content: 'Content',
+ enterContent: 'Please enter prompt content',
+ referenceImage: 'Reference Image',
+ imageTip: 'Supports JPG and PNG formats, max size 2MB',
+ active: 'Active',
+ deleteConfirm: 'Are you sure you want to delete this prompt?',
+ deleteSuccess: 'Deleted successfully',
+ saveSuccess: 'Saved successfully',
+ activeSuccess: 'Set to active status',
+ inactiveSuccess: 'Removed from active status'
+ },
+ productManagement: {
+ title: 'Product Management',
+ addProduct: 'Add Product',
+ editProduct: 'Edit Product',
+ productDetail: 'Product Detail',
+ productId: 'Product ID',
+ productName: 'Product Name',
+ enterProductName: 'Enter Product Name',
+ productImage: 'Product Image',
+ imageUploadTip: 'Support JPG, PNG, GIF format, max size 5MB',
+ imageTypeError: 'Please upload image file only',
+ imageSizeError: 'Image size cannot exceed 5MB',
+ imageUploadSuccess: 'Image uploaded successfully',
+ imageUploadFailed: 'Failed to upload image',
+ noImage: 'No image',
+ description: 'Description',
+ enterDescription: 'Enter Description',
+ amount: 'Amount',
+ enterAmount: 'Enter Amount',
+ currency: 'Currency',
+ selectCurrency: 'Select Currency',
+ price: 'Price',
+ status: 'Status',
+ selectStatus: 'Select Status',
+ active: 'Active',
+ deleted: 'Deleted',
+ createdAt: 'Created At',
+ updatedAt: 'Updated At',
+ updatePrice: 'Update Price',
+ newAmount: 'New Amount',
+ enterNewAmount: 'Enter New Amount',
+ currentPrice: 'Current Price',
+ fetchFailed: 'Failed to fetch products',
+ addSuccess: 'Product added successfully',
+ addFailed: 'Failed to add product',
+ updateSuccess: 'Product updated successfully',
+ updateFailed: 'Failed to update product',
+ deleteSuccess: 'Product deleted successfully',
+ deleteFailed: 'Failed to delete product',
+ priceUpdateSuccess: 'Price updated successfully',
+ priceUpdateFailed: 'Failed to update price',
+ detailFailed: 'Failed to fetch product detail',
+ deleteConfirm: 'Are you sure you want to delete this product?'
}
},
modelUpload: {
diff --git a/apps/FrontendDesigner/src/locales/lang/zh-CN.js b/apps/FrontendDesigner/src/locales/lang/zh-CN.js
index ae2ea7f..23d9ff3 100644
--- a/apps/FrontendDesigner/src/locales/lang/zh-CN.js
+++ b/apps/FrontendDesigner/src/locales/lang/zh-CN.js
@@ -278,6 +278,7 @@ orderManagement: {
pointsManagement: '充值包管理',
commissionManagement: '佣金管理',
promptManagement: '提示词管理',
+ productManagement: '产品管理',
logout: '退出登录',
profile: '个人资料',
settings: '设置',
@@ -790,6 +791,7 @@ orderManagement: {
animal: '动物',
person: '人物',
general: '通用',
+ O1: 'O1',
title: '标题',
enterTitle: '请输入提示词标题',
content: '内容',
@@ -802,6 +804,50 @@ orderManagement: {
saveSuccess: '保存成功',
activeSuccess: '已设置为生效状态',
inactiveSuccess: '已从生效状态移除'
+ },
+ productManagement: {
+ title: '产品管理',
+ addProduct: '添加产品',
+ editProduct: '编辑产品',
+ productDetail: '产品详情',
+ productId: '产品ID',
+ productName: '产品名称',
+ enterProductName: '请输入产品名称',
+ productImage: '产品图片',
+ imageUploadTip: '支持JPG、PNG、GIF格式,最大5MB',
+ imageTypeError: '请上传图片文件',
+ imageSizeError: '图片大小不能超过5MB',
+ imageUploadSuccess: '图片上传成功',
+ imageUploadFailed: '图片上传失败',
+ noImage: '暂无图片',
+ description: '描述',
+ enterDescription: '请输入产品描述',
+ amount: '金额',
+ enterAmount: '请输入金额',
+ currency: '货币',
+ selectCurrency: '选择货币',
+ price: '价格',
+ status: '状态',
+ selectStatus: '选择状态',
+ active: '活跃',
+ deleted: '已删除',
+ createdAt: '创建时间',
+ updatedAt: '更新时间',
+ updatePrice: '更新价格',
+ newAmount: '新金额',
+ enterNewAmount: '请输入新金额',
+ currentPrice: '当前价格',
+ fetchFailed: '获取产品列表失败',
+ addSuccess: '产品添加成功',
+ addFailed: '产品添加失败',
+ updateSuccess: '产品更新成功',
+ updateFailed: '产品更新失败',
+ deleteSuccess: '产品删除成功',
+ deleteFailed: '产品删除失败',
+ priceUpdateSuccess: '价格更新成功',
+ priceUpdateFailed: '价格更新失败',
+ detailFailed: '获取产品详情失败',
+ deleteConfirm: '确定要删除这个产品吗?'
}
},
modelUpload: {
@@ -854,6 +900,8 @@ orderManagement: {
save: '保存',
delete: '删除',
edit: '编辑',
+ detail: '详情',
+ actions: '操作',
search: '搜索',
loading: '加载中...',
viewAll: '查看全部',
@@ -887,6 +935,7 @@ orderManagement: {
exportAsOBJ: '导出为OBJ',
exportAsSTL: '导出为STL',
exportAsFBX: '导出为FBX',
+ exportAs3MF: '导出为3MF',
selectFormat: '选择导出格式',
preparingExport: '准备导出中...'
},
diff --git a/apps/FrontendDesigner/src/main.js b/apps/FrontendDesigner/src/main.js
index 02c7ac1..6f3ce7a 100644
--- a/apps/FrontendDesigner/src/main.js
+++ b/apps/FrontendDesigner/src/main.js
@@ -56,8 +56,10 @@ const initRoutes = () => {
}
}
}
-
-initRoutes()
+if (window.location.hostname.indexOf('local') === -1) {
+ initRoutes()
+}
+// initRoutes()
// 配置路由
app.use(router)
diff --git a/apps/FrontendDesigner/src/router/index.js b/apps/FrontendDesigner/src/router/index.js
index ae1e336..98e70f6 100644
--- a/apps/FrontendDesigner/src/router/index.js
+++ b/apps/FrontendDesigner/src/router/index.js
@@ -23,6 +23,7 @@ const AdminUserList = () => import('@/views/admin/AdminRoleManagement/AdminUserL
const AdminPointsManagement = () => import('@/views/admin/AdminPointsManagement/AdminPointsManagement.vue')
const AdminCommissionManagement = () => import('@/views/admin/AdminCommissionManagement/AdminCommissionManagement.vue')
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement/AdminPromptManagement.vue')
+const AdminProductManagement = () => import('@/views/admin/ProductManagement/ProductManagement.vue')
//权限路由映射表
export const permissionRoutes = [
{
@@ -35,6 +36,17 @@ export const permissionRoutes = [
requiresAuth: true,
menuOrder: 1
}
+ },
+ {
+ path: 'product-management',
+ name: 'AdminProductManagement',
+ component: AdminProductManagement,
+ meta: {
+ title: 'admin.layout.productManagement',
+ icon: 'ShoppingCartFull',
+ menuOrder: 2,
+ requiresAuth: true
+ }
},
{
path: 'orders',
@@ -58,17 +70,7 @@ export const permissionRoutes = [
requiresAuth: true,
},
},
- {
- path: 'disassembly-orders',
- name: 'AdminDisassemblyOrders',
- component: AdminDisassemblyOrders,
- meta: {
- title: 'admin.layout.disassemblyOrders',
- icon: 'EditPen',
- menuOrder: 2,
- requiresAuth: true,
- }
- },
+
{
path: 'order-list',
name: 'AdminOrdersList',
@@ -126,6 +128,7 @@ export const permissionRoutes = [
requiresAuth: true
}
},
+
{
path: 'permission',
name: 'AdminPermission',
@@ -222,6 +225,17 @@ const routes = [
requiresAuth: true
},
children: [
+ {
+ path: 'disassembly-orders',
+ name: 'AdminDisassemblyOrders',
+ component: AdminDisassemblyOrders,
+ meta: {
+ title: 'admin.layout.disassemblyOrders',
+ icon: 'EditPen',
+ menuOrder: 2,
+ requiresAuth: true,
+ }
+ },
{
path: 'disassembly-orders/:id',
name: 'AdminDisassemblyDetail',
@@ -269,6 +283,7 @@ const routes = [
requiresAuth: true
}
},
+ ...(window.location.hostname.indexOf('local') === -1 ? [] : permissionRoutes)
]//[]//
},
{
diff --git a/apps/FrontendDesigner/src/stores/index.js b/apps/FrontendDesigner/src/stores/index.js
index a8dae04..bf6fe9e 100644
--- a/apps/FrontendDesigner/src/stores/index.js
+++ b/apps/FrontendDesigner/src/stores/index.js
@@ -11,7 +11,7 @@ export const useAuthStore = defineStore('auth', {
permissionButton: JSON.parse(localStorage.getItem('permissionButton') || '[]'),
router: null,
routesUpdated: 0,//路由更新次数
- routerList: []//侧边栏路由
+ routerList: window.location.hostname.indexOf('local') === -1 ? [] : permissionRoutes,//侧边栏路由
}),
getters: {
diff --git a/apps/FrontendDesigner/src/views/admin/AdminPromptManagement/AdminPromptManagement.vue b/apps/FrontendDesigner/src/views/admin/AdminPromptManagement/AdminPromptManagement.vue
index 298bd25..28c10c0 100644
--- a/apps/FrontendDesigner/src/views/admin/AdminPromptManagement/AdminPromptManagement.vue
+++ b/apps/FrontendDesigner/src/views/admin/AdminPromptManagement/AdminPromptManagement.vue
@@ -82,6 +82,7 @@
+
diff --git a/apps/FrontendDesigner/src/views/admin/ProductManagement/ProductManagement.vue b/apps/FrontendDesigner/src/views/admin/ProductManagement/ProductManagement.vue
new file mode 100644
index 0000000..cd340d8
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/admin/ProductManagement/ProductManagement.vue
@@ -0,0 +1,737 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.search') }}
+
+
+ {{ t('common.reset') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${{ scope.row.current_price.amount }} {{ scope.row.current_price.currency }}
+
+ -
+
+
+
+
+ {{ formatDate(scope.row.created_at) }}
+
+
+
+
+
+
+ {{ t('common.detail') }}
+
+
+
+ {{ t('common.edit') }}
+
+
+
+ {{ t('common.delete') }}
+
+
+
+ {{ t('admin.productManagement.updatePrice') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('admin.productManagement.imageUploadTip') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ selectedProduct.id }}
+
+
+ {{ selectedProduct.name }}
+
+
+
+ {{ t('admin.productManagement.noImage') }}
+
+
+ {{ selectedProduct.description }}
+
+
+ ${{ selectedProduct.current_price.amount }} {{ selectedProduct.current_price.currency }}
+
+
+
+ {{ formatDate(selectedProduct.created_at) }}
+
+
+ {{ formatDate(selectedProduct.updated_at) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/FrontendDesigner/src/views/admin/ProductManagement/index.js b/apps/FrontendDesigner/src/views/admin/ProductManagement/index.js
new file mode 100644
index 0000000..cf3462a
--- /dev/null
+++ b/apps/FrontendDesigner/src/views/admin/ProductManagement/index.js
@@ -0,0 +1,62 @@
+import { requestUtils, adminApi } from '@deotaland/utils';
+export class ProductManagement {
+ //创建产品及价格
+ async createProduct(data) {
+ let params = {
+ name: data.name,//产品名称
+ description: data.description,//产品描述
+ amount: data.amount,//金额,单位为美元
+ currency: data.currency,//货币类型,默认USD
+ product_info:{
+ image:data.image,//产品图片
+ }
+ }
+ return await requestUtils.common(adminApi.default.createProduct, params);
+ }
+ //获取产品详情
+ async getProductDetail(data) {
+ let params = {
+ id: data.id,
+ }
+ return await requestUtils.common(adminApi.default.getProductDetail, params);
+ }
+ //获取产品列表
+ async getProductList(data) {
+ let params = {
+ "page": data.page,//页码
+ "page_size": data.page_size,//每页数量
+ "name": data.name,//产品名称
+ "is_delete": data.is_delete,//是否删除
+ }
+ return await requestUtils.common(adminApi.default.getProductList, params);
+ }
+ //更新产品信息
+ async updateProduct(data) {
+ let params = {
+ "id": data.id,//产品ID
+ "name": data.name,//产品名称
+ "description": data.description,//产品描述
+ "current_price_id": data.current_price_id,//当前价格ID
+ product_info:{
+ image:data.image,//产品图片
+ }
+ }
+ return await requestUtils.common(adminApi.default.updateProduct, params);
+ }
+ //更新产品价格
+ async updateProductPrice(data) {
+ let params = {
+ "product_id": data.product_id,//产品ID
+ "amount": data.amount,//金额,单位为美元
+ "currency": data.currency,//货币类型,默认USD
+ }
+ return await requestUtils.common(adminApi.default.updateProductPrice, params);
+ }
+ //删除产品
+ async deleteProduct(data){
+ let params = {
+ "id": data.id,//产品ID
+ }
+ return await requestUtils.common(adminApi.default.deleteProduct, params);
+ }
+}
\ No newline at end of file
diff --git a/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue b/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
index 2b97d53..cacc13d 100644
--- a/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
+++ b/apps/frontend/src/components/HeaderComponent/HeaderComponent.vue
@@ -68,8 +68,7 @@ import { Picture, MagicStick, ArrowLeft, Edit, Check, Guide } from '@element-plu
import { ElButton, ElIcon, ElInput } from 'element-plus'
import ThemeToggle from '../ui/ThemeToggle.vue'
import LanguageToggle from '../ui/LanguageToggle.vue'
-
-const emit = defineEmits(['openGuideModal'])
+const emit = defineEmits(['openGuideModal','back'])
const props = defineProps({
total_score: {
type: Number,
@@ -92,12 +91,13 @@ const { t, locale } = useI18n()
// 处理返回按钮点击
const handleBack = () => {
// 返回上一页或首页
- if (window.history.length > 1) {
- window.history.back()
- } else {
- // 如果没有历史记录,则跳转到首页
- window.location.href = '/'
- }
+ // if (window.history.length > 1) {
+ // window.history.back()
+ // } else {
+ // // 如果没有历史记录,则跳转到首页
+ // window.location.href = '/'
+ // }
+ emit('back')
}
// 开始编辑项目名称
diff --git a/apps/frontend/src/components/PurchaseModal/index.vue b/apps/frontend/src/components/PurchaseModal/index.vue
index 3d01768..8110904 100644
--- a/apps/frontend/src/components/PurchaseModal/index.vue
+++ b/apps/frontend/src/components/PurchaseModal/index.vue
@@ -21,11 +21,10 @@
{{ $t('checkout.customModel') }}
- {{ $t('checkout.from') }} ${{ (amountCents ).toFixed(2) }}
+ {{ $t('checkout.from') }} {{unt=='USD'?'$':unt}} {{ (amountCents ).toFixed(2) }}
-
@@ -178,10 +177,12 @@ import StripePaymentForm from '@/components/StripePaymentForm.vue'
import { Country, State } from 'country-state-city'
import { useI18n } from 'vue-i18n'
import { PayServer } from '@deotaland/utils'
+import { requestUtils,clientApi } from '@deotaland/utils'
const payserver = new PayServer();
const props = defineProps({
modelData: { type: Object, default: () => ({}) },
- show: { type: Boolean, default: false }
+ show: { type: Boolean, default: false },
+ series: { type: String, default: '' }
})
const emit = defineEmits(['close'])
const onClose = () => emit('close')
@@ -198,7 +199,7 @@ const showPayingOverlay = ref(false)
// 省州映射数据已移至国际化文件
const amountCents = computed(() => {
- let base = 299 // 默认价格,移除了尺寸相关定价
+ let base = price.value // 默认价格,移除了尺寸相关定价
if (addons.value.gloss) base += 300
if (addons.value.base) base += 400
if (addons.value.matte) base += 200
@@ -219,6 +220,22 @@ const isPayButtonDisabled = computed(() => {
ipName.value.trim()
)
})
+const unt = ref('');
+const price = ref(0);
+const seriesId = ref('');
+//获取对应价格
+const getPrice = async () => {
+ const res = await requestUtils.common(clientApi.default.getProductList)
+ if(res.code === 0){
+ const data = res.data.list || []
+ const item = data.find(item => item.name === props.series)
+ if(item){
+ price.value = item.price?.amount || 0
+ unt.value = item.price?.currency || ''
+ seriesId.value = item.id || ''
+ }
+ }
+}
const incQty = () => { qty.value = Math.min(qty.value+1, 99) }
const decQty = () => { qty.value = Math.max(qty.value-1, 1) }
const goShopify = () => {//用户点击购买
@@ -265,6 +282,8 @@ const goShopify = () => {//用户点击购买
// Save shipping and contact information if checkbox is checked
saveLocal()
// 在控制台打印整理后的信息
+ console.log('Order Parameters:', params)
+ params.product_id = seriesId.value
payserver.createPayorOrder(params);
}
@@ -298,6 +317,7 @@ onMounted(() => {
if (c) Object.assign(contact.value, JSON.parse(c))
updateCountryOptions()
updateStates()
+ getPrice();
} catch (e) {}
})
watch(() => shipping.value.country, () => { updateStates() })
diff --git a/apps/frontend/src/components/SeriesSelector.vue b/apps/frontend/src/components/SeriesSelector.vue
index 162e451..b74b0b2 100644
--- a/apps/frontend/src/components/SeriesSelector.vue
+++ b/apps/frontend/src/components/SeriesSelector.vue
@@ -12,26 +12,26 @@
-
-

+
-
Done
-
-
-
-
-
-

+
+
{{ series.name }}
+
{{ series.description }}
+
+ {{ formatPrice(series.price?.amount, series.price?.currency) }}
+
-
Oone
@@ -41,9 +41,9 @@
\ No newline at end of file
diff --git a/apps/frontend/src/components/layout/AppHeader.vue b/apps/frontend/src/components/layout/AppHeader.vue
index f72d74a..13c8feb 100644
--- a/apps/frontend/src/components/layout/AppHeader.vue
+++ b/apps/frontend/src/components/layout/AppHeader.vue
@@ -415,6 +415,12 @@ export default {
transition: all 0.2s ease;
}
+.mobile-menu-button svg {
+ width: 24px;
+ height: 24px;
+ flex-shrink: 0;
+}
+
.mobile-menu-button:hover {
background: var(--hover-bg, #f3f4f6);
}
diff --git a/apps/frontend/src/components/layout/AppSidebar.vue b/apps/frontend/src/components/layout/AppSidebar.vue
index 14ad43c..ca731ef 100644
--- a/apps/frontend/src/components/layout/AppSidebar.vue
+++ b/apps/frontend/src/components/layout/AppSidebar.vue
@@ -79,7 +79,8 @@ import {
DataAnalysis as AnalyticsIcon,
Folder as ProjectIcon,
Bell as NotificationIcon,
- Key as ApiIcon
+ Key as ApiIcon,
+ ShoppingCartFull
} from '@element-plus/icons-vue'
// 定义组件名称(用于调试和递归组件)
@@ -112,10 +113,11 @@ const remainingPoints = ref(1280)
const currentUser = computed(() => authStore.user)
const userRole = computed(() => ({
'1': 'free',
- '2': 'creator',
-}[authStore.user?.user_role || '1'])) // 可切换为 'free' 测试不同样式
+ '2':'creator'
+}[authStore.user?.user_role || authStore.user?.userRole||'1'])) // 可切换为 'free' 测试不同样式
const sidebarClasses = computed(() => ({
- 'sidebar-mobile': isMobile.value
+ 'sidebar-mobile': isMobile.value,
+ 'show': isMobile.value && !props.collapsed
}))
// 核心菜单项 (6个主要功能)
@@ -208,7 +210,7 @@ onUnmounted(() => {
height: 100%;
background: var(--sidebar-bg, #ffffff);
border-right: 1px solid var(--border-color, #e5e7eb);
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ /* transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); */
position: relative;
overflow: hidden;
z-index: 100;
@@ -640,12 +642,14 @@ onUnmounted(() => {
position: fixed;
left: 0;
top: 0;
+ height: 100vh;
z-index: 1000;
transform: translateX(-100%);
box-shadow: 4px 0 24px rgba(107, 70, 193, 0.2);
- transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
+ /* transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); */
backdrop-filter: blur(12px);
border-right: 1px solid rgba(107, 70, 193, 0.1);
+ padding-top: 64px;
}
.sidebar-mobile.show {
@@ -683,28 +687,53 @@ onUnmounted(() => {
@media (max-width: 767px) {
.app-sidebar {
width: 100%;
- max-width: 320px;
+ height: 100vh;
+ }
+
+ .sidebar-nav {
+ padding: 16px 8px;
+ height: calc(100vh - 64px);
+ overflow-y: auto;
}
.nav-item {
- padding: 16px 20px;
- min-height: 72px;
- margin: 8px 16px;
+ /* padding: 12px 8px; */
+ min-height: 50px;
+ /* margin: 6px 4px; */
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 0;
+ max-height: 50px;
}
.nav-icon {
- width: 32px;
- height: 32px;
- margin-bottom: 8px;
+ width: 24px;
+ height: 24px;
+ margin-bottom: 0;
+ flex-shrink: 0;
}
.nav-icon svg {
- width: 22px;
- height: 22px;
+ width: 20px;
+ height: 20px;
}
.nav-text {
- font-size: 15px;
+ display: none;
+ }
+
+ .sidebar-footer {
+ padding: 12px 8px;
+ }
+
+ .user-avatar-container {
+ width: 32px;
+ height: 32px;
+ }
+
+ .role-badge {
+ display: none;
}
}
diff --git a/apps/frontend/src/components/layout/MainLayout.vue b/apps/frontend/src/components/layout/MainLayout.vue
index 4f1c609..cbbcd8f 100644
--- a/apps/frontend/src/components/layout/MainLayout.vue
+++ b/apps/frontend/src/components/layout/MainLayout.vue
@@ -14,6 +14,7 @@