Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
925d6b105f | ||
|
|
9ede62f450 | ||
|
|
43471c3a76 | ||
|
|
9f472390d9 | ||
|
|
fbb14eafe4 | ||
|
|
ab7987bd54 | ||
|
|
04d89f4a9a | ||
|
|
b52dcc0842 | ||
|
|
c30768d605 | ||
|
|
571d4a72b3 | ||
|
|
b86cc0e7a9 | ||
|
|
bff6520a18 | ||
|
|
1c89f495a7 | ||
|
|
8ae713aef9 |
No files matched your search
@@ -1,7 +1,9 @@
|
|||||||
VITE_APP_PREVIEW=true
|
VITE_APP_PREVIEW=true
|
||||||
# VITE_APP_API_BASE_URL=http://172.16.20.21:8166/web/
|
# VITE_APP_API_BASE_URL=http://172.16.20.21:20001/api/web/
|
||||||
|
# VITE_APP_API_BASE_URL=http://172.16.0.40:20001/api/web/
|
||||||
|
VITE_APP_API_BASE_URL=https://shop.api.suwa3d.com/api/web/
|
||||||
# VITE_APP_API_BASE_URL=https://wechat-test.api.puabadge.com/web/
|
# VITE_APP_API_BASE_URL=https://wechat-test.api.puabadge.com/web/
|
||||||
VITE_APP_API_BASE_URL=https://wechat.api.puabadge.com/web/
|
# VITE_APP_API_BASE_URL=https://wechat.api.puabadge.com/web/
|
||||||
# VITE_APP_API_WX_URL=https://wechat.api.puabadge.com/web/
|
# VITE_APP_API_WX_URL=https://wechat.api.puabadge.com/web/
|
||||||
# http://web.suwa3d.dev:28499/
|
# http://web.suwa3d.dev:28499/
|
||||||
VITE_HTTP_MOCK=true
|
VITE_HTTP_MOCK=true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
VITE_APP_PREVIEW=false
|
VITE_APP_PREVIEW=false
|
||||||
VITE_APP_API_BASE_URL=https://wechat.api.puabadge.com/web/
|
VITE_APP_API_BASE_URL=https://shop.api.suwa3d.com/api/web/
|
||||||
VITE_APP_API_URL=https://wechat.api.puabadge.com/web/
|
VITE_APP_API_URL=https://shop.api.suwa3d.com/api/web/
|
||||||
VITE_APP_API_WX_URL=https://wechat.api.puabadge.com/web/
|
VITE_APP_API_WX_URL=https://shop.api.suwa3d.com/api/web/
|
||||||
WECHAT_AUTH_URL=https://wechat.api.puabadge.com/api/auth/page
|
WECHAT_AUTH_URL=https://shop.api.suwa3d.com/api/auth/page
|
||||||
@@ -13,6 +13,16 @@ declare module '@vue/runtime-core' {
|
|||||||
Backup: typeof import('./src/components/arFrame/backup.vue')['default']
|
Backup: typeof import('./src/components/arFrame/backup.vue')['default']
|
||||||
Backup2: typeof import('./src/components/arFrame/backup2.vue')['default']
|
Backup2: typeof import('./src/components/arFrame/backup2.vue')['default']
|
||||||
Brand: typeof import('./src/components/brand/index.vue')['default']
|
Brand: typeof import('./src/components/brand/index.vue')['default']
|
||||||
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
|
ElImage: typeof import('element-plus/es')['ElImage']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||||
|
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||||
Gsplat: typeof import('./src/components/arFrame/gsplat.vue')['default']
|
Gsplat: typeof import('./src/components/arFrame/gsplat.vue')['default']
|
||||||
Hint: typeof import('./src/components/hint/hint.vue')['default']
|
Hint: typeof import('./src/components/hint/hint.vue')['default']
|
||||||
Loading: typeof import('./src/components/loading/index.vue')['default']
|
Loading: typeof import('./src/components/loading/index.vue')['default']
|
||||||
@@ -22,11 +32,11 @@ declare module '@vue/runtime-core' {
|
|||||||
Tensorflow: typeof import('./src/components/arFrame/tensorflow.vue')['default']
|
Tensorflow: typeof import('./src/components/arFrame/tensorflow.vue')['default']
|
||||||
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
||||||
VanAddressEdit: typeof import('vant/es')['AddressEdit']
|
VanAddressEdit: typeof import('vant/es')['AddressEdit']
|
||||||
VanButton: typeof import('vant/es')['Button']
|
|
||||||
VanDivider: typeof import('vant/es')['Divider']
|
VanDivider: typeof import('vant/es')['Divider']
|
||||||
VanField: typeof import('vant/es')['Field']
|
|
||||||
VanIcon: typeof import('vant/es')['Icon']
|
VanIcon: typeof import('vant/es')['Icon']
|
||||||
VanList: typeof import('vant/es')['List']
|
VanList: typeof import('vant/es')['List']
|
||||||
|
VanLoading: typeof import('vant/es')['Loading']
|
||||||
|
VanOverlay: typeof import('vant/es')['Overlay']
|
||||||
VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
||||||
VanStepper: typeof import('vant/es')['Stepper']
|
VanStepper: typeof import('vant/es')['Stepper']
|
||||||
VanUploader: typeof import('vant/es')['Uploader']
|
VanUploader: typeof import('vant/es')['Uploader']
|
||||||
|
|||||||
@@ -159,3 +159,43 @@ export const getMultiUrl = (params: any) => {
|
|||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取坐骑列表
|
||||||
|
export const getMountsList = (params: any) => {
|
||||||
|
return request('cartoon/mountsList', {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成多视图数据
|
||||||
|
export const generateViews = (data: any) => {
|
||||||
|
return request('views/generateViews', {
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多视图列表
|
||||||
|
export const listViews = (params: any) => {
|
||||||
|
return request('views/listViews', {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水平翻转图片
|
||||||
|
export const flipViews = (params: any) => {
|
||||||
|
return request('views/flipViews', {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认后视图
|
||||||
|
export const confirmViews = (params: any) => {
|
||||||
|
return request('views/confirmViews', {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 315 B |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 279 B |
@@ -0,0 +1,466 @@
|
|||||||
|
// 卡通手办页面配置
|
||||||
|
export const cartoonConfig = {
|
||||||
|
// 路由配置
|
||||||
|
routes: {
|
||||||
|
record: '/cartoon/photoAlbum',
|
||||||
|
myOrder: '/cartoon/myOrder',
|
||||||
|
preview: '/cartoon/previewOrder',
|
||||||
|
orderDetail: '/cartoon/orderDetail',
|
||||||
|
home: '/cartoon'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 产品类型配置
|
||||||
|
productTypes: {
|
||||||
|
1: { name: '立体徽章', key: 'badge_3d' },
|
||||||
|
2: { name: '浮雕相框', key: 'relief_frame' },
|
||||||
|
3: { name: '3D冰箱贴', key: 'fridge_magnet' },
|
||||||
|
4: { name: '3D卡通手办', key: 'cartoon_figure' },
|
||||||
|
6: { name: '浮雕挂饰', key: 'relief_pendant' },
|
||||||
|
8: { name: '摇摇乐', key: 'shake_toy' }
|
||||||
|
},
|
||||||
|
|
||||||
|
// 主体类型配置
|
||||||
|
subjectTypes: {
|
||||||
|
person: {
|
||||||
|
id: 7,
|
||||||
|
name: '人物主体',
|
||||||
|
prodIds: [7, 1, 25], // 不同产品类型对应的人物产品ID
|
||||||
|
group: 'gp',
|
||||||
|
supportSubject: 1
|
||||||
|
},
|
||||||
|
pet: {
|
||||||
|
id: 8,
|
||||||
|
name: '宠物主体',
|
||||||
|
prodIds: [8, 2, 19, 24], // 不同产品类型对应的宠物产品ID
|
||||||
|
group: 'pet',
|
||||||
|
supportSubject: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 产品ID映射配置(根据typeId和subjectId映射到实际产品ID)
|
||||||
|
productIdMapping: {
|
||||||
|
4: { // typeId == 4 (3D卡通手办)
|
||||||
|
person: 1,
|
||||||
|
pet: 19
|
||||||
|
},
|
||||||
|
6: { // typeId == 6 (浮雕挂饰)
|
||||||
|
person: 25,
|
||||||
|
pet: 24
|
||||||
|
},
|
||||||
|
3: { // typeId == 3 (3D冰箱贴)
|
||||||
|
person: 7,
|
||||||
|
pet: 8
|
||||||
|
},
|
||||||
|
default: { // 其他类型
|
||||||
|
person: 7,
|
||||||
|
pet: 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 产品限制配置
|
||||||
|
productLimits: {
|
||||||
|
// 根据产品ID配置是否支持多视角参考图
|
||||||
|
multiViewSupported: [19],
|
||||||
|
// 根据产品ID配置是否支持风格类型
|
||||||
|
kindListSupported: [4],
|
||||||
|
// 根据typeId配置是否显示照片示例
|
||||||
|
photoExampleTypes: ['3D真人肖像']
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传配置
|
||||||
|
upload: {
|
||||||
|
minWidth: 300,
|
||||||
|
minHeight: 300,
|
||||||
|
maxSize: 10 * 1024 * 1024, // 10MB
|
||||||
|
maxReferCount: 3,
|
||||||
|
cropOptions: {
|
||||||
|
canScale: true,
|
||||||
|
autoCropWidth: 1024,
|
||||||
|
autoCropHeight: 1024,
|
||||||
|
ceilbutton: true,
|
||||||
|
infoTrue: true,
|
||||||
|
fixed: false,
|
||||||
|
fixedNumber: [3, 4],
|
||||||
|
fixedBox: false,
|
||||||
|
canMoveBox: true,
|
||||||
|
enlarge: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片资源路径配置(在组件中通过 import 导入)
|
||||||
|
assets: {
|
||||||
|
sheji: '@/assets/badge/sheji.png',
|
||||||
|
order: '@/assets/badge/order.png'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 样式配置
|
||||||
|
styles: {
|
||||||
|
colors: {
|
||||||
|
primary: '#50cf54',
|
||||||
|
primaryGradient: 'linear-gradient(90deg, #D1ED8E 0%, #55E668 100%)',
|
||||||
|
textPrimary: '#222',
|
||||||
|
textSecondary: '#333',
|
||||||
|
textTertiary: '#888',
|
||||||
|
textDisabled: '#b0b0b0',
|
||||||
|
background: '#F0F2F5',
|
||||||
|
backgroundSecondary: '#F2F2F2',
|
||||||
|
border: '#E5E5E5',
|
||||||
|
success: '#15CF5F',
|
||||||
|
progressBar: '#6fdc8c'
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
pagePadding: '16px',
|
||||||
|
sectionMargin: '8px',
|
||||||
|
itemGap: '8px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Preview页面配置
|
||||||
|
preview: {
|
||||||
|
// 图片尺寸配置
|
||||||
|
imageSize: {
|
||||||
|
previewWidth: 320,
|
||||||
|
previewHeight: 320,
|
||||||
|
compareImageSize: 120
|
||||||
|
},
|
||||||
|
|
||||||
|
// 进度条配置
|
||||||
|
progress: {
|
||||||
|
maxProgress: 90,
|
||||||
|
interval: 300, // ms
|
||||||
|
thresholds: {
|
||||||
|
stage1: 25,
|
||||||
|
stage2: 50,
|
||||||
|
stage3: 75
|
||||||
|
},
|
||||||
|
stages: {
|
||||||
|
stage1: '照片数据分析中',
|
||||||
|
stage2: '提炼图像关键词',
|
||||||
|
stage3: '图像生成中',
|
||||||
|
stage4: '图像后处理与优化'
|
||||||
|
},
|
||||||
|
totalTime: 90 // 秒
|
||||||
|
},
|
||||||
|
|
||||||
|
// 形状ID配置
|
||||||
|
shapeIds: {
|
||||||
|
round: [1, 2], // 圆形形状
|
||||||
|
specialFrame: [15, 16], // 特殊相框
|
||||||
|
frame: [17, 18], // 相框
|
||||||
|
topBottomText: [27, 28] // 支持上下文字的形状
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文字配置默认值
|
||||||
|
textConfig: {
|
||||||
|
front: {
|
||||||
|
x: 163,
|
||||||
|
y: 222,
|
||||||
|
width: 700,
|
||||||
|
height: 700,
|
||||||
|
radius: 350,
|
||||||
|
fontSize: 72,
|
||||||
|
maxLength: 18
|
||||||
|
},
|
||||||
|
back: {
|
||||||
|
x: 238,
|
||||||
|
y: 297,
|
||||||
|
width: 550,
|
||||||
|
height: 550,
|
||||||
|
radius: 275,
|
||||||
|
fontSize: 60,
|
||||||
|
maxLength: 56
|
||||||
|
},
|
||||||
|
defaultColor: '#000000',
|
||||||
|
defaultWeight: 'normal'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片缩放比例
|
||||||
|
imageScale: 1,
|
||||||
|
|
||||||
|
// 默认值配置
|
||||||
|
defaults: {
|
||||||
|
group: 1,
|
||||||
|
imgKey: 101,
|
||||||
|
countryId: 45,
|
||||||
|
addrId: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
// 轮询配置
|
||||||
|
polling: {
|
||||||
|
interval: 10000, // 10秒
|
||||||
|
compareImageDelay: 1000 // 1秒
|
||||||
|
},
|
||||||
|
|
||||||
|
// 特殊code配置(用于地址识别功能)
|
||||||
|
specialCodes: {
|
||||||
|
addressRecognition: 'ACAYOAAC3PFCPO3CD6DVM1'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 产品名称映射(根据typeId和prodId)
|
||||||
|
productNames: {
|
||||||
|
1: { // typeId == 1 (立体徽章)
|
||||||
|
7: '人物立体徽章',
|
||||||
|
8: '宠物立体徽章'
|
||||||
|
},
|
||||||
|
2: { // typeId == 2 (浮雕相框)
|
||||||
|
7: '人物浮雕相框',
|
||||||
|
8: '宠物浮雕相框'
|
||||||
|
},
|
||||||
|
3: { // typeId == 3 (3D冰箱贴)
|
||||||
|
7: '人物3D冰箱贴',
|
||||||
|
8: '宠物3D冰箱贴'
|
||||||
|
},
|
||||||
|
4: { // typeId == 4 (3D卡通手办)
|
||||||
|
1: '人物卡通手办',
|
||||||
|
19: '宠物卡通手办'
|
||||||
|
},
|
||||||
|
6: { // typeId == 6 (浮雕挂饰)
|
||||||
|
25: '人物浮雕挂饰',
|
||||||
|
24: '宠物浮雕挂饰'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
messages: {
|
||||||
|
invalidInput: '输入的文字不能包含特殊符号!',
|
||||||
|
selectQuantity: '请先选择下单数量',
|
||||||
|
fillContactName: '请先填写收货人信息',
|
||||||
|
fillContactMobile: '请先填写收货人手机号',
|
||||||
|
selectArea: '请先选择地区',
|
||||||
|
fillAddress: '请先填写详细地址',
|
||||||
|
orderSuccess: '下单成功',
|
||||||
|
noRemainingCount: '当前次数已用完,请重新购买',
|
||||||
|
confirmRegenerate: '请确认是否重新生成。',
|
||||||
|
confirmOrder: '请再次确认是否选择这个模型下单,下单后预计需要10天完成生产发货。如对产品有疑问,可以联系客服咨询。',
|
||||||
|
addressPlaceholder: '请输入姓名、手机号、详细地址(如:张三 13800138000 广东省深圳市南山区科技园1号)',
|
||||||
|
addressRecognitionResult: '识别结果',
|
||||||
|
compareTip: '温馨提示:AI设计的徽章效果图是根据用户提供的照片机器学习生成的,多试几次就能找到你满意的徽章~',
|
||||||
|
saveImageTip: '长按图片 → 保存到相册',
|
||||||
|
designing: '设计中',
|
||||||
|
designFailed: '设计失败',
|
||||||
|
remainingExchange: '剩余兑换',
|
||||||
|
process: '工艺',
|
||||||
|
productType: '产品类型',
|
||||||
|
orderQuantity: '订购数量',
|
||||||
|
addressRecognition: '地址识别',
|
||||||
|
shippingAddress: '收货地址',
|
||||||
|
regenerate: '再次生成',
|
||||||
|
compare: '前后对比',
|
||||||
|
saveImage: '保存图片',
|
||||||
|
confirmSelect: '确认选择',
|
||||||
|
originalImage: '原图',
|
||||||
|
previewImage: '效果图',
|
||||||
|
badgePreview: '徽章预览'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// PhotoAlbum页面配置
|
||||||
|
photoAlbum: {
|
||||||
|
// 分页配置
|
||||||
|
pagination: {
|
||||||
|
pageSize: 20,
|
||||||
|
initialPage: 1
|
||||||
|
},
|
||||||
|
|
||||||
|
// 状态配置
|
||||||
|
status: {
|
||||||
|
success: 1, // 设计成功
|
||||||
|
designing: 0, // 设计中
|
||||||
|
failed: 2 // 设计失败
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片资源路径(在组件中通过 import 导入)
|
||||||
|
assets: {
|
||||||
|
nullData: '@/assets/badge/null-data.png'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 样式配置
|
||||||
|
styles: {
|
||||||
|
gridColumns: 2, // 网格列数
|
||||||
|
gap: 16, // 间距
|
||||||
|
borderRadius: 12 // 圆角
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
messages: {
|
||||||
|
title: '设计记录',
|
||||||
|
note: '注:可点击效果图进行下单打印',
|
||||||
|
noMore: '没有更多了',
|
||||||
|
loading: '加载中...',
|
||||||
|
noData: '暂无数据',
|
||||||
|
designing: '设计中',
|
||||||
|
designFailed: '设计失败',
|
||||||
|
getDataFailed: '获取数据失败'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// MyOrder页面配置
|
||||||
|
myOrder: {
|
||||||
|
// 分页配置
|
||||||
|
pagination: {
|
||||||
|
pageSize: 30,
|
||||||
|
initialPage: 1,
|
||||||
|
defaultStatus: 0 // 默认订单状态
|
||||||
|
},
|
||||||
|
|
||||||
|
// 订单状态配置
|
||||||
|
orderStatus: {
|
||||||
|
pending: 1, // 待支付
|
||||||
|
cancelled: 2, // 已取消
|
||||||
|
producing: 100, // 生产中
|
||||||
|
shipping: 200, // 待收货
|
||||||
|
completed: 300 // 已完成
|
||||||
|
},
|
||||||
|
|
||||||
|
// 订单状态文本映射
|
||||||
|
statusText: {
|
||||||
|
1: '待支付',
|
||||||
|
2: '已取消',
|
||||||
|
100: '生产中',
|
||||||
|
200: '待收货',
|
||||||
|
300: '已完成'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 订单状态样式配置
|
||||||
|
statusStyles: {
|
||||||
|
processing: {
|
||||||
|
background: '#bdbdbd' // 处理中状态背景色
|
||||||
|
},
|
||||||
|
finished: {
|
||||||
|
background: '#8bc34a' // 完成状态背景色
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
background: '#bdbdbd',
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片资源路径(在组件中通过 import 导入)
|
||||||
|
assets: {
|
||||||
|
nullData: '@/assets/badge/null-data.png'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 样式配置
|
||||||
|
styles: {
|
||||||
|
gap: 8, // 间距
|
||||||
|
itemWidth: 'calc(50% - 4px)', // 订单项宽度
|
||||||
|
itemHeight: '43vw', // 订单项高度
|
||||||
|
borderRadius: 12, // 圆角
|
||||||
|
padding: {
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 24
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
bottom: 5,
|
||||||
|
right: 5,
|
||||||
|
padding: {
|
||||||
|
vertical: 2,
|
||||||
|
horizontal: 12
|
||||||
|
},
|
||||||
|
borderRadius: 12,
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
shadow: '0 2px 8px rgba(0,0,0,0.08)' // 阴影
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
messages: {
|
||||||
|
title: '我的订单',
|
||||||
|
noData: '暂无数据'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// OrderDetail页面配置
|
||||||
|
orderDetail: {
|
||||||
|
// 订单类型配置
|
||||||
|
orderTypes: {
|
||||||
|
shipping: 2 // 需要收货地址的订单类型
|
||||||
|
},
|
||||||
|
|
||||||
|
// 产品类型配置(需要特殊显示的产品类型)
|
||||||
|
productTypes: {
|
||||||
|
reliefPendant: 6 // 浮雕挂饰(需要显示双面)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片尺寸配置
|
||||||
|
imageSize: {
|
||||||
|
width: 320,
|
||||||
|
height: 320,
|
||||||
|
borderRadius: 10
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片缩放比例
|
||||||
|
imageScale: 0.6,
|
||||||
|
|
||||||
|
// 样式配置
|
||||||
|
styles: {
|
||||||
|
padding: {
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 0
|
||||||
|
},
|
||||||
|
statusInfo: {
|
||||||
|
padding: {
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 0
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#222',
|
||||||
|
marginBottom: 8
|
||||||
|
},
|
||||||
|
desc: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#888'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
productInfo: {
|
||||||
|
padding: {
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 0
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#111',
|
||||||
|
marginBottom: 10
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
gap: 8
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: '#bdbdbd',
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
color: '#222',
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
gap: 10,
|
||||||
|
borderRadius: 10
|
||||||
|
},
|
||||||
|
bottomSpacing: 30
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
messages: {
|
||||||
|
title: '订单详情',
|
||||||
|
statusDesc: '下单后预计需要10天完成生产发货。如对产品有疑问,可以联系客服咨询。',
|
||||||
|
shippingInfo: '收货信息',
|
||||||
|
contactInfo: '收货人信息',
|
||||||
|
shippingAddress: '收货地址',
|
||||||
|
productInfo: '产品信息',
|
||||||
|
productName: '产品名称',
|
||||||
|
size: '尺寸',
|
||||||
|
shape: '形状',
|
||||||
|
quantity: '购买数量',
|
||||||
|
orderInfo: '订单信息',
|
||||||
|
orderId: '购买订单id',
|
||||||
|
orderTime: '购买时间'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -149,5 +149,59 @@ const en = {
|
|||||||
"日漫3D": "Japanese 3D",
|
"日漫3D": "Japanese 3D",
|
||||||
"迪斯尼皮克斯": "Disney Pixar",
|
"迪斯尼皮克斯": "Disney Pixar",
|
||||||
"潮玩IP": "IP Toy",
|
"潮玩IP": "IP Toy",
|
||||||
|
"浮雕挂饰": "Relief Pendant",
|
||||||
|
"人物浮雕挂饰": "Character Relief Pendant",
|
||||||
|
"宠物浮雕挂饰": "Pet Relief Pendant",
|
||||||
|
"浮雕挂饰-正面": "Relief Pendant-Front",
|
||||||
|
"浮雕挂饰-背面": "Relief Pendant-Back",
|
||||||
|
"请输入正面文字": "Please enter front text",
|
||||||
|
"请输入背面文字": "Please enter back text",
|
||||||
|
"请输入顶部文字": "Please enter top text",
|
||||||
|
"请输入底部文字": "Please enter bottom text",
|
||||||
|
"坐骑类型": "Ride Type",
|
||||||
|
"智能生成": "Smart Generation",
|
||||||
|
"自定义": "Custom",
|
||||||
|
"请选择坐骑类型": "Please select ride type",
|
||||||
|
"场景道具": "Scene Props",
|
||||||
|
"请输入场景道具": "Please enter scene props",
|
||||||
|
"手办摇摇乐": "Shake Toy",
|
||||||
|
"手办挂饰": "Handheld Pendant",
|
||||||
|
"场景手办": "Scene Figure",
|
||||||
|
"A款底座": "A Base",
|
||||||
|
"B款底座": "B Base",
|
||||||
|
"C款底座": "C Base",
|
||||||
|
"D款底座": "D Base",
|
||||||
|
"E款底座": "E Base",
|
||||||
|
"F款底座": "F Base",
|
||||||
|
"后视图": "Rear View",
|
||||||
|
"多主体手办下单前需先生成正确的后视图": "Multi-subject figures require generating the correct rear view before placing an order",
|
||||||
|
"请输入提示词,描述需要生成的后视图要求": "Please enter a prompt describing the requirements for generating the rear view",
|
||||||
|
"编辑": "Edit",
|
||||||
|
"生成后视图": "Generate Rear View",
|
||||||
|
"预设提示词": "Preset Prompt",
|
||||||
|
"请等待前一张后视图生成完成": "Please wait for the previous rear view to finish generating",
|
||||||
|
"订单ID不能为空": "Order ID cannot be empty",
|
||||||
|
"后视图生成提交成功": "Rear view generation submitted successfully",
|
||||||
|
"生成后视图提交失败,请重试": "Failed to submit rear view generation, please try again",
|
||||||
|
"镜像处理成功": "Mirror processing successful",
|
||||||
|
"镜像处理失败": "Mirror processing failed",
|
||||||
|
"请先选择后视图": "Please select a rear view first",
|
||||||
|
"后视图确认成功": "Rear view confirmed successfully",
|
||||||
|
"后视图确认失败": "Rear view confirmation failed",
|
||||||
|
"请输入后视图提示词": "Please enter rear view prompt",
|
||||||
|
"请输入后视图提示词,描述需要生成的后视图要求": "Please enter a rear view prompt describing the requirements for generating the rear view",
|
||||||
|
"选择预设提示词": "Select Preset Prompt",
|
||||||
|
"提示词": "Prompt",
|
||||||
|
"暂无预设提示词": "No preset prompts available",
|
||||||
|
"取消": "Cancel",
|
||||||
|
"后视图要求": "Rear View Requirements",
|
||||||
|
"1. 后视图应与前视图保持角色和动作的一致性": "1. The rear view should maintain character and action consistency with the front view",
|
||||||
|
"2. 模型需要水平旋转180度": "2. The model needs to be rotated 180 degrees horizontally",
|
||||||
|
"3. 确保后视图清晰可见,无明显遮挡": "3. Ensure the rear view is clear and visible with no obvious obstructions",
|
||||||
|
"4. 后视图应展示角色的背面特征": "4. The rear view should display the character's back features",
|
||||||
|
"镜像": "Mirror",
|
||||||
|
"选择": "Select",
|
||||||
|
"镜像处理": "Mirror Processing",
|
||||||
|
"确认选择后视图": "Confirm Rear View Selection",
|
||||||
}
|
}
|
||||||
export default en
|
export default en
|
||||||
@@ -149,5 +149,59 @@ const ko = {
|
|||||||
"日漫3D": "일본 3D",
|
"日漫3D": "일본 3D",
|
||||||
"迪斯尼皮克斯": "디즈니 픽사",
|
"迪斯尼皮克斯": "디즈니 픽사",
|
||||||
"潮玩IP": "IP 토이",
|
"潮玩IP": "IP 토이",
|
||||||
|
"浮雕挂饰": "부조 걸이",
|
||||||
|
"人物浮雕挂饰": "캐릭터 부조 걸이",
|
||||||
|
"宠物浮雕挂饰": "애완동물 부조 걸이",
|
||||||
|
"浮雕挂饰-正面": "부조 걸이-정면",
|
||||||
|
"浮雕挂饰-背面": "부조 걸이-뒷면",
|
||||||
|
"请输入正面文字": "정면 텍스트를 입력하세요",
|
||||||
|
"请输入背面文字": "뒷면 텍스트를 입력하세요",
|
||||||
|
"请输入顶部文字": "상단 텍스트를 입력하세요",
|
||||||
|
"请输入底部文字": "하단 텍스트를 입력하세요",
|
||||||
|
"坐骑类型": "좌석 유형",
|
||||||
|
"智能生成": "스마트 생성",
|
||||||
|
"自定义": "사용자 정의",
|
||||||
|
"请选择坐骑类型": "좌석 유형을 선택하세요",
|
||||||
|
"场景道具": "장소 도구",
|
||||||
|
"请输入场景道具": "장소 도구를 입력하세요",
|
||||||
|
"手办摇摇乐": "피규어 흔들림",
|
||||||
|
"手办挂饰": "피규어 걸이",
|
||||||
|
"场景手办": "장소 피규어",
|
||||||
|
"A款底座": "A 베이스",
|
||||||
|
"B款底座": "B 베이스",
|
||||||
|
"C款底座": "C 베이스",
|
||||||
|
"D款底座": "D 베이스",
|
||||||
|
"E款底座": "E 베이스",
|
||||||
|
"F款底座": "F 베이스",
|
||||||
|
"后视图": "후면 뷰",
|
||||||
|
"多主体手办下单前需先生成正确的后视图": "다중 주체 피규어는 주문 전에 올바른 후면 뷰를 먼저 생성해야 합니다",
|
||||||
|
"请输入提示词,描述需要生成的后视图要求": "후면 뷰 생성 요구사항을 설명하는 프롬프트를 입력하세요",
|
||||||
|
"编辑": "편집",
|
||||||
|
"生成后视图": "후면 뷰 생성",
|
||||||
|
"预设提示词": "사전 설정 프롬프트",
|
||||||
|
"请等待前一张后视图生成完成": "이전 후면 뷰 생성이 완료될 때까지 기다려주세요",
|
||||||
|
"订单ID不能为空": "주문 ID는 비어 있을 수 없습니다",
|
||||||
|
"后视图生成提交成功": "후면 뷰 생성 제출 성공",
|
||||||
|
"生成后视图提交失败,请重试": "후면 뷰 생성 제출 실패, 다시 시도해주세요",
|
||||||
|
"镜像处理成功": "미러 처리 성공",
|
||||||
|
"镜像处理失败": "미러 처리 실패",
|
||||||
|
"请先选择后视图": "먼저 후면 뷰를 선택하세요",
|
||||||
|
"后视图确认成功": "후면 뷰 확인 성공",
|
||||||
|
"后视图确认失败": "후면 뷰 확인 실패",
|
||||||
|
"请输入后视图提示词": "후면 뷰 프롬프트를 입력하세요",
|
||||||
|
"请输入后视图提示词,描述需要生成的后视图要求": "생성할 후면 뷰 요구사항을 설명하는 후면 뷰 프롬프트를 입력하세요",
|
||||||
|
"选择预设提示词": "사전 설정 프롬프트 선택",
|
||||||
|
"提示词": "프롬프트",
|
||||||
|
"暂无预设提示词": "사전 설정 프롬프트 없음",
|
||||||
|
"取消": "취소",
|
||||||
|
"后视图要求": "후면 뷰 요구사항",
|
||||||
|
"1. 后视图应与前视图保持角色和动作的一致性": "1. 후면 뷰는 전면 뷰와 캐릭터 및 동작의 일관성을 유지해야 합니다",
|
||||||
|
"2. 模型需要水平旋转180度": "2. 모델은 수평으로 180도 회전해야 합니다",
|
||||||
|
"3. 确保后视图清晰可见,无明显遮挡": "3. 후면 뷰가 명확하고 보이며 명백한 가림이 없어야 합니다",
|
||||||
|
"4. 后视图应展示角色的背面特征": "4. 후면 뷰는 캐릭터의 뒷면 특징을 보여줘야 합니다",
|
||||||
|
"镜像": "미러",
|
||||||
|
"选择": "선택",
|
||||||
|
"镜像处理": "미러 처리",
|
||||||
|
"确认选择后视图": "후면 뷰 선택 확인",
|
||||||
}
|
}
|
||||||
export default ko
|
export default ko
|
||||||
@@ -149,5 +149,62 @@ const zhCN = {
|
|||||||
"日漫3D": "日漫3D",
|
"日漫3D": "日漫3D",
|
||||||
"迪斯尼皮克斯": "迪斯尼皮克斯",
|
"迪斯尼皮克斯": "迪斯尼皮克斯",
|
||||||
"潮玩IP": "潮玩IP",
|
"潮玩IP": "潮玩IP",
|
||||||
|
"浮雕挂饰": "浮雕挂饰",
|
||||||
|
"人物浮雕挂饰": "人物浮雕挂饰",
|
||||||
|
"宠物浮雕挂饰": "宠物浮雕挂饰",
|
||||||
|
"浮雕挂饰-正面": "浮雕挂饰-正面",
|
||||||
|
"浮雕挂饰-背面": "浮雕挂饰-背面",
|
||||||
|
"请输入正面文字": "请输入正面文字",
|
||||||
|
"请输入背面文字": "请输入背面文字",
|
||||||
|
"请输入顶部文字": "请输入顶部文字",
|
||||||
|
"请输入底部文字": "请输入底部文字",
|
||||||
|
"坐骑类型": "坐骑类型",
|
||||||
|
"智能生成": "智能生成",
|
||||||
|
"自定义": "自定义",
|
||||||
|
"请选择坐骑类型": "请选择坐骑类型",
|
||||||
|
"场景道具": "场景道具",
|
||||||
|
"请输入场景道具": "请输入场景道具",
|
||||||
|
"手办摇摇乐": "手办摇摇乐",
|
||||||
|
"手办挂饰": "手办挂饰",
|
||||||
|
"场景手办": "场景手办",
|
||||||
|
"A款底座": "A款底座",
|
||||||
|
"B款底座": "B款底座",
|
||||||
|
"C款底座": "C款底座",
|
||||||
|
"D款底座": "D款底座",
|
||||||
|
"E款底座": "E款底座",
|
||||||
|
"F款底座": "F款底座",
|
||||||
|
"G款底座": "G款底座",
|
||||||
|
"H款底座": "H款底座",
|
||||||
|
"I款底座": "I款底座",
|
||||||
|
"后视图": "后视图",
|
||||||
|
"多主体手办下单前需先生成正确的后视图": "多主体手办下单前需先生成正确的后视图",
|
||||||
|
"请输入提示词,描述需要生成的后视图要求": "请输入提示词,描述需要生成的后视图要求",
|
||||||
|
"编辑": "编辑",
|
||||||
|
"生成后视图": "生成后视图",
|
||||||
|
"预设提示词": "预设提示词",
|
||||||
|
"请等待前一张后视图生成完成": "请等待前一张后视图生成完成",
|
||||||
|
"订单ID不能为空": "订单ID不能为空",
|
||||||
|
"后视图生成提交成功": "后视图生成提交成功",
|
||||||
|
"生成后视图提交失败,请重试": "生成后视图提交失败,请重试",
|
||||||
|
"镜像处理成功": "镜像处理成功",
|
||||||
|
"镜像处理失败": "镜像处理失败",
|
||||||
|
"请先选择后视图": "请先选择后视图",
|
||||||
|
"后视图确认成功": "后视图确认成功",
|
||||||
|
"后视图确认失败": "后视图确认失败",
|
||||||
|
"请输入后视图提示词": "请输入后视图提示词",
|
||||||
|
"请输入后视图提示词,描述需要生成的后视图要求": "请输入后视图提示词,描述需要生成的后视图要求",
|
||||||
|
"选择预设提示词": "选择预设提示词",
|
||||||
|
"提示词": "提示词",
|
||||||
|
"暂无预设提示词": "暂无预设提示词",
|
||||||
|
"取消": "取消",
|
||||||
|
"后视图要求": "后视图要求",
|
||||||
|
"1. 后视图应与前视图保持角色和动作的一致性": "1. 后视图应与前视图保持角色和动作的一致性",
|
||||||
|
"2. 模型需要水平旋转180度": "2. 模型需要水平旋转180度",
|
||||||
|
"3. 确保后视图清晰可见,无明显遮挡": "3. 确保后视图清晰可见,无明显遮挡",
|
||||||
|
"4. 后视图应展示角色的背面特征": "4. 后视图应展示角色的背面特征",
|
||||||
|
"镜像": "镜像",
|
||||||
|
"选择": "选择",
|
||||||
|
"镜像处理": "镜像处理",
|
||||||
|
"确认选择后视图": "确认选择后视图",
|
||||||
}
|
}
|
||||||
export default zhCN
|
export default zhCN
|
||||||
@@ -149,5 +149,59 @@ const zhCT = {
|
|||||||
"日漫3D": "日漫3D",
|
"日漫3D": "日漫3D",
|
||||||
"迪斯尼皮克斯": "迪士尼皮克斯",
|
"迪斯尼皮克斯": "迪士尼皮克斯",
|
||||||
"潮玩IP": "潮玩IP",
|
"潮玩IP": "潮玩IP",
|
||||||
|
"浮雕挂饰": "浮雕掛飾",
|
||||||
|
"人物浮雕挂饰": "人物浮雕掛飾",
|
||||||
|
"宠物浮雕挂饰": "寵物浮雕掛飾",
|
||||||
|
"浮雕挂饰-正面": "浮雕掛飾-正面",
|
||||||
|
"浮雕挂饰-背面": "浮雕掛飾-背面",
|
||||||
|
"请输入正面文字": "請輸入正面文字",
|
||||||
|
"请输入背面文字": "請輸入背面文字",
|
||||||
|
"请输入顶部文字": "請輸入頂部文字",
|
||||||
|
"请输入底部文字": "請輸入底部文字",
|
||||||
|
"坐骑类型": "坐騎類型",
|
||||||
|
"智能生成": "智能生成",
|
||||||
|
"自定义": "自定義",
|
||||||
|
"请选择坐骑类型": "請選擇坐騎類型",
|
||||||
|
"场景道具": "場景道具",
|
||||||
|
"请输入场景道具": "請輸入場景道具",
|
||||||
|
"手办摇摇乐": "手辦搖搖樂",
|
||||||
|
"手办挂饰": "手辦掛飾",
|
||||||
|
"场景手办": "場景手辦",
|
||||||
|
"A款底座": "A款底座",
|
||||||
|
"B款底座": "B款底座",
|
||||||
|
"C款底座": "C款底座",
|
||||||
|
"D款底座": "D款底座",
|
||||||
|
"E款底座": "E款底座",
|
||||||
|
"F款底座": "F款底座",
|
||||||
|
"后视图": "後視圖",
|
||||||
|
"多主体手办下单前需先生成正确的后视图": "多主體手辦下單前需先生成正確的後視圖",
|
||||||
|
"请输入提示词,描述需要生成的后视图要求": "請輸入提示詞,描述需要生成的後視圖要求",
|
||||||
|
"编辑": "編輯",
|
||||||
|
"生成后视图": "生成後視圖",
|
||||||
|
"预设提示词": "預設提示詞",
|
||||||
|
"请等待前一张后视图生成完成": "請等待前一張後視圖生成完成",
|
||||||
|
"订单ID不能为空": "訂單ID不能為空",
|
||||||
|
"后视图生成提交成功": "後視圖生成提交成功",
|
||||||
|
"生成后视图提交失败,请重试": "生成後視圖提交失敗,請重試",
|
||||||
|
"镜像处理成功": "鏡像處理成功",
|
||||||
|
"镜像处理失败": "鏡像處理失敗",
|
||||||
|
"请先选择后视图": "請先選擇後視圖",
|
||||||
|
"后视图确认成功": "後視圖確認成功",
|
||||||
|
"后视图确认失败": "後視圖確認失敗",
|
||||||
|
"请输入后视图提示词": "請輸入後視圖提示詞",
|
||||||
|
"请输入后视图提示词,描述需要生成的后视图要求": "請輸入後視圖提示詞,描述需要生成的後視圖要求",
|
||||||
|
"选择预设提示词": "選擇預設提示詞",
|
||||||
|
"提示词": "提示詞",
|
||||||
|
"暂无预设提示词": "暫無預設提示詞",
|
||||||
|
"取消": "取消",
|
||||||
|
"后视图要求": "後視圖要求",
|
||||||
|
"1. 后视图应与前视图保持角色和动作的一致性": "1. 後視圖應與前視圖保持角色和動作的一致性",
|
||||||
|
"2. 模型需要水平旋转180度": "2. 模型需要水平旋轉180度",
|
||||||
|
"3. 确保后视图清晰可见,无明显遮挡": "3. 確保後視圖清晰可見,無明顯遮擋",
|
||||||
|
"4. 后视图应展示角色的背面特征": "4. 後視圖應展示角色的背面特徵",
|
||||||
|
"镜像": "鏡像",
|
||||||
|
"选择": "選擇",
|
||||||
|
"镜像处理": "鏡像處理",
|
||||||
|
"确认选择后视图": "確認選擇後視圖",
|
||||||
}
|
}
|
||||||
export default zhCT
|
export default zhCT
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createApp, onMounted } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
import { createRouterScroller } from 'vue-router-better-scroller'
|
import { createRouterScroller } from 'vue-router-better-scroller'
|
||||||
@@ -105,13 +105,34 @@ app.use(createRouterScroller({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
onMounted(() => {
|
// 处理页面隐藏事件(可以区分刷新和关闭)
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
function handlePageHide(event: PageTransitionEvent) {
|
||||||
});
|
// event.persisted 为 true 表示页面被缓存(刷新),保留 code
|
||||||
|
// event.persisted 为 false 表示页面未被缓存(关闭),清除 code
|
||||||
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
if (!event.persisted) {
|
||||||
|
// 页面关闭(非刷新),清除 code 和 userId,避免下一个用户使用上一个用户的 code
|
||||||
localStorage.remove('code')
|
localStorage.remove('code')
|
||||||
localStorage.remove('trialCode')
|
localStorage.remove('userId')
|
||||||
|
console.log('页面关闭,已清除 code 和 userId')
|
||||||
|
} else {
|
||||||
|
// 页面刷新(persisted === true),保留 code
|
||||||
|
console.log('页面刷新,保留 code')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理页面卸载事件(作为备用方案,某些浏览器可能不支持 pagehide)
|
||||||
|
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
||||||
|
// 注意:beforeunload 无法区分刷新和关闭
|
||||||
|
// 主要依赖 pagehide 事件来处理
|
||||||
|
// 这里不做任何操作,避免误清除刷新时的 code
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在应用挂载前设置事件监听器
|
||||||
|
// 优先使用 pagehide 事件(可以区分刷新和关闭)
|
||||||
|
// 在支持的浏览器中,这是最可靠的方法
|
||||||
|
window.addEventListener('pagehide', handlePageHide)
|
||||||
|
|
||||||
|
// 监听 beforeunload 作为备用(某些旧浏览器可能不支持 pagehide)
|
||||||
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// https://router.vuejs.org/zh/
|
||||||
|
import 'nprogress/nprogress.css'
|
||||||
|
|
||||||
|
import cartoon from '@/views/cartoon/index.vue'
|
||||||
|
import previewOrder from '@/views/cartoon/previewOrder.vue'
|
||||||
|
import photoAlbum from '@/views/cartoon/photoAlbum.vue'
|
||||||
|
import myOrder from '@/views/cartoon/myOrder.vue'
|
||||||
|
import orderDetail from '@/views/cartoon/orderDetail.vue'
|
||||||
|
// 定义路由,每个路由都需要映射到一个组件
|
||||||
|
export const routes = [
|
||||||
|
{
|
||||||
|
path: '/cartoon',
|
||||||
|
name: 'cartoon',
|
||||||
|
component: cartoon,
|
||||||
|
meta: {
|
||||||
|
needGuard: true,
|
||||||
|
title: '卡通手办',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cartoon/previewOrder',
|
||||||
|
name: 'previewOrder',
|
||||||
|
component: previewOrder,
|
||||||
|
meta: {
|
||||||
|
needGuard: true,
|
||||||
|
title: '预览效果图',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cartoon/photoAlbum',
|
||||||
|
name: 'photoAlbum',
|
||||||
|
component: photoAlbum,
|
||||||
|
meta: {
|
||||||
|
needGuard: true,
|
||||||
|
title: '设计记录',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cartoon/myOrder',
|
||||||
|
name: 'myOrder',
|
||||||
|
component: myOrder,
|
||||||
|
meta: {
|
||||||
|
needGuard: true,
|
||||||
|
title: '我的订单',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cartoon/orderDetail',
|
||||||
|
name: 'orderDetail',
|
||||||
|
component: orderDetail,
|
||||||
|
meta: {
|
||||||
|
needGuard: true,
|
||||||
|
title: '订单详情',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export function mergeRoutes(allRoutes: any[]) {
|
||||||
|
allRoutes.push(...routes)
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@ import { createRouter, createWebHashHistory } from 'vue-router'
|
|||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
// 导入路由组件
|
// 导入路由组件
|
||||||
import * as badgeRouter from './badge'
|
// import * as badgeRouter from './badge'
|
||||||
import badge from '@/views/badge/index.vue'
|
import * as cartoonRouter from './cartoon'
|
||||||
|
// import badge from '@/views/badge/index.vue'
|
||||||
|
import cartoon from '@/views/cartoon/index.vue'
|
||||||
import {useStore} from '@/stores'
|
import {useStore} from '@/stores'
|
||||||
import { config } from '@/config/config'
|
import { config } from '@/config/config'
|
||||||
import { localStorage } from '@/utils/local-storage'
|
import { localStorage } from '@/utils/local-storage'
|
||||||
@@ -15,8 +17,8 @@ NProgress.configure({ showSpinner: true, parent: '#app' })
|
|||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'badge',
|
name: 'cartoon',
|
||||||
component: badge,
|
component: cartoon,
|
||||||
meta: {
|
meta: {
|
||||||
needGuard: true,
|
needGuard: true,
|
||||||
title: '首页',
|
title: '首页',
|
||||||
@@ -24,7 +26,8 @@ const routes = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
badgeRouter.mergeRoutes(routes)
|
// badgeRouter.mergeRoutes(routes)
|
||||||
|
cartoonRouter.mergeRoutes(routes)
|
||||||
// 创建路由实例并传递 `routes` 配置
|
// 创建路由实例并传递 `routes` 配置
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(process.env.VUE_APP_PUBLIC_PATH),
|
history: createWebHashHistory(process.env.VUE_APP_PUBLIC_PATH),
|
||||||
@@ -35,18 +38,25 @@ router.beforeEach((_to, _from, next) => {
|
|||||||
// const tokenStore = useTokenStore()
|
// const tokenStore = useTokenStore()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const code = _to.query.code
|
const code = _to.query.code
|
||||||
|
|
||||||
|
// 如果 URL 中有 code 参数,保存到 localStorage(用于刷新时保留)
|
||||||
|
if (code && typeof code === 'string') {
|
||||||
|
localStorage.set('code', code)
|
||||||
|
console.log('保存 code 到 localStorage:', code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 URL 中没有 code,尝试从 localStorage 读取(用于刷新时恢复)
|
||||||
|
const savedCode = localStorage.get('code')
|
||||||
|
if (!code && savedCode) {
|
||||||
|
console.log('从 localStorage 恢复 code:', savedCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('当前 code===>', code || savedCode)
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
localStorage.set('code', code as string);
|
|
||||||
}
|
|
||||||
const trialCode = _to.query.trialCode
|
|
||||||
if (trialCode) {
|
|
||||||
localStorage.set('trialCode', trialCode as string);
|
|
||||||
}
|
|
||||||
console.log('code===>', code);
|
|
||||||
if (code || trialCode) {
|
|
||||||
let url = store.redirectUrl()
|
let url = store.redirectUrl()
|
||||||
|
|
||||||
if (_to.path !== '/badge') {
|
if (_to.path !== '/cartoon') {
|
||||||
if (url) {
|
if (url) {
|
||||||
store.setRedirect('')
|
store.setRedirect('')
|
||||||
next(url)
|
next(url)
|
||||||
|
|||||||
@@ -50,18 +50,18 @@ const errorHandler = (error: RequestError): Promise<any> => {
|
|||||||
|
|
||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
const requestHandler = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig> => {
|
const requestHandler = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig> => {
|
||||||
// const savedToken = localStorage.get(STORAGE_TOKEN_KEY)
|
// 从 localStorage 读取 code(刷新时也能获取到)
|
||||||
// 如果 token 存在
|
|
||||||
const code = localStorage.get('code')
|
const code = localStorage.get('code')
|
||||||
const trialCode = localStorage.get('trialCode')
|
|
||||||
const userId = localStorage.get('userId')
|
const userId = localStorage.get('userId')
|
||||||
|
|
||||||
// 让每个请求携带自定义 token, 请根据实际情况修改
|
// 让每个请求携带自定义 token, 请根据实际情况修改
|
||||||
if (code)
|
if (code) {
|
||||||
config.headers['Authorization'] = code
|
config.headers['Authorization'] = code
|
||||||
if (trialCode)
|
}
|
||||||
config.headers['Authorization'] = trialCode
|
if (userId) {
|
||||||
if (userId)
|
|
||||||
config.headers['Userid'] = userId
|
config.headers['Userid'] = userId
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,586 @@
|
|||||||
|
// textHelper.ts
|
||||||
|
/**
|
||||||
|
* 文字定位助手模块
|
||||||
|
* @module TextPosition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文字定位助手实例
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @param {Function} options.setData - 设置数据的方法(用于更新响应式数据)
|
||||||
|
* @returns {Object} 包含renderText方法的对象
|
||||||
|
*/
|
||||||
|
export function createTextPosition({ setData }) {
|
||||||
|
if (!setData) {
|
||||||
|
throw new Error('setData is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认样式配置
|
||||||
|
const defaultStyle = {
|
||||||
|
fontSize: 16, // 字体大小(px)
|
||||||
|
fontFamily: 'Arial', // 字体
|
||||||
|
color: '#000000', // 文字颜色
|
||||||
|
textAlign: 'left', // 文本对齐方式: left, center, right
|
||||||
|
fontWeight: 'normal', // 字体粗细: normal, bold
|
||||||
|
lineHeight: 1.5, // 行高
|
||||||
|
opacity: 1, // 透明度
|
||||||
|
backgroundColor: 'transparent', // 背景颜色
|
||||||
|
padding: 0, // 内边距(px)
|
||||||
|
borderRadius: 0, // 边框圆角(px)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染文字到指定位置
|
||||||
|
* @param {Object} options - 文字配置
|
||||||
|
* @param {string} options.text - 要显示的文字
|
||||||
|
* @param {number} options.x - 文字左上角X坐标(基于原始容器尺寸)
|
||||||
|
* @param {number} options.y - 文字左上角Y坐标(基于原始容器尺寸)
|
||||||
|
* @param {number} [options.width] - 文字容器宽度(基于原始容器尺寸)
|
||||||
|
* @param {number} [options.height] - 文字容器高度(基于原始容器尺寸)
|
||||||
|
* @param {Object} [options.style] - 文字样式配置
|
||||||
|
* @param {string} [options.bindKey] - 绑定到页面数据的键名
|
||||||
|
*/
|
||||||
|
|
||||||
|
function roundText({ shapeImage, x, y, width, height, radius, style = {}, bindKey = 'roundTextConfig', text = '', isFront = true, cachedImageInfo = null, onImageInfoCached = null }) {
|
||||||
|
// 合并默认样式和用户自定义样式
|
||||||
|
const mergedStyle = { ...defaultStyle, ...style };
|
||||||
|
|
||||||
|
// 获取设备信息,计算缩放比例
|
||||||
|
const screenWidth = 320;
|
||||||
|
|
||||||
|
// 计算宽度和高度的缩放比例
|
||||||
|
const scale = 0.6;
|
||||||
|
|
||||||
|
// 计算字符位置的函数
|
||||||
|
const calculateTextPosition = (imageInfo) => {
|
||||||
|
// 计算图片宽高比
|
||||||
|
const imageRatio = imageInfo.width / imageInfo.height;
|
||||||
|
const containerRatio = 1; // 容器是正方形,宽高比为1
|
||||||
|
|
||||||
|
let ratio;
|
||||||
|
|
||||||
|
// 根据图片和容器的比例关系决定如何缩放
|
||||||
|
if (imageRatio > containerRatio) {
|
||||||
|
// 图片更宽,以宽度为基准缩放
|
||||||
|
ratio = screenWidth / imageInfo.width;
|
||||||
|
} else {
|
||||||
|
// 图片更高或等比,以高度为基准缩放
|
||||||
|
ratio = screenWidth / imageInfo.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算缩放后的参数
|
||||||
|
const scaledX = x * ratio * scale;
|
||||||
|
const scaledY = y * ratio * scale;
|
||||||
|
const scaledWidth = width * ratio * scale;
|
||||||
|
const scaledHeight = height * ratio * scale;
|
||||||
|
const scaledRadius = radius * ratio * scale;
|
||||||
|
const scaledFontSize = mergedStyle.fontSize * ratio * scale;
|
||||||
|
|
||||||
|
// 计算圆心位置(基于容器的中心点)
|
||||||
|
const centerX = scaledX + scaledWidth / 2;
|
||||||
|
const centerY = scaledY + scaledHeight / 2;
|
||||||
|
|
||||||
|
// 计算每个字符的位置信息
|
||||||
|
// 根据正面/背面使用不同的计算方法,互不影响
|
||||||
|
const charList = isFront
|
||||||
|
? calculateFrontRoundTextChars({
|
||||||
|
text: text,
|
||||||
|
centerX: scaledWidth / 2, // 相对于容器的中心
|
||||||
|
centerY: scaledHeight / 2,
|
||||||
|
radius: scaledRadius,
|
||||||
|
fontSize: scaledFontSize,
|
||||||
|
fontWeight: mergedStyle.fontWeight || 'normal',
|
||||||
|
color: mergedStyle.color || '#000000'
|
||||||
|
})
|
||||||
|
: calculateBackRoundTextChars({
|
||||||
|
text: text,
|
||||||
|
centerX: scaledWidth / 2, // 相对于容器的中心
|
||||||
|
centerY: scaledHeight / 2,
|
||||||
|
radius: scaledRadius,
|
||||||
|
fontSize: scaledFontSize,
|
||||||
|
fontWeight: mergedStyle.fontWeight || 'normal',
|
||||||
|
color: mergedStyle.color || '#000000'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建圆形文字配置对象
|
||||||
|
const roundTextConfig = {
|
||||||
|
x: scaledX,
|
||||||
|
y: scaledY,
|
||||||
|
width: scaledWidth,
|
||||||
|
height: scaledHeight,
|
||||||
|
centerX: centerX,
|
||||||
|
centerY: centerY,
|
||||||
|
radius: scaledRadius,
|
||||||
|
text: text,
|
||||||
|
charList: charList, // 字符位置列表
|
||||||
|
style: {
|
||||||
|
...mergedStyle,
|
||||||
|
fontSize: scaledFontSize,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('roundTextConfig===>', roundTextConfig);
|
||||||
|
|
||||||
|
// 更新页面数据
|
||||||
|
setData({
|
||||||
|
[bindKey]: roundTextConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
// 返回配置对象,以便后续使用
|
||||||
|
return roundTextConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果已有缓存的图片信息,直接使用
|
||||||
|
if (cachedImageInfo && cachedImageInfo.src === shapeImage) {
|
||||||
|
calculateTextPosition(cachedImageInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则获取图片信息
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const res = {
|
||||||
|
width: img.width,
|
||||||
|
height: img.height,
|
||||||
|
path: shapeImage,
|
||||||
|
src: shapeImage
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果提供了缓存回调,调用它
|
||||||
|
if (typeof onImageInfoCached === 'function') {
|
||||||
|
onImageInfoCached(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateTextPosition(res);
|
||||||
|
};
|
||||||
|
img.onerror = (err) => {
|
||||||
|
console.error('Failed to get image info:', err);
|
||||||
|
};
|
||||||
|
img.src = shapeImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算正面圆形文字中每个字符的位置和样式
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @param {string} options.text - 文字内容
|
||||||
|
* @param {number} options.centerX - 圆心X坐标(相对于容器)
|
||||||
|
* @param {number} options.centerY - 圆心Y坐标(相对于容器)
|
||||||
|
* @param {number} options.radius - 半径
|
||||||
|
* @param {number} options.fontSize - 字体大小
|
||||||
|
* @param {string} options.fontWeight - 字体粗细
|
||||||
|
* @param {string} options.color - 文字颜色
|
||||||
|
* @returns {Array} 字符位置信息数组
|
||||||
|
*/
|
||||||
|
function calculateFrontRoundTextChars({ text, centerX, centerY, radius, fontSize, fontWeight, color }) {
|
||||||
|
if (!text) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算字符宽度
|
||||||
|
// 使用更精确的估算方法
|
||||||
|
const charWidths = [];
|
||||||
|
let totalWidth = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
let charWidth;
|
||||||
|
|
||||||
|
// 判断字符类型,使用不同的宽度估算
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(char)) {
|
||||||
|
// 中文字符:宽度约为 fontSize
|
||||||
|
charWidth = fontSize;
|
||||||
|
} else if (/[0-9]/.test(char)) {
|
||||||
|
// 数字:宽度约为 fontSize * 0.6
|
||||||
|
charWidth = fontSize * 0.6;
|
||||||
|
} else if (/[a-zA-Z]/.test(char)) {
|
||||||
|
// 英文字母:宽度约为 fontSize * 0.6(大写)或 0.5(小写)
|
||||||
|
charWidth = /[A-Z]/.test(char) ? fontSize * 0.6 : fontSize * 0.45;
|
||||||
|
} else if (/[\u3000-\u303f\uff00-\uffef]/.test(char)) {
|
||||||
|
// 全角符号:宽度约为 fontSize
|
||||||
|
charWidth = fontSize;
|
||||||
|
} else {
|
||||||
|
// 其他字符(半角符号等):宽度约为 fontSize * 0.4
|
||||||
|
charWidth = fontSize * 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字体是粗体,稍微增加宽度
|
||||||
|
if (fontWeight === 'bold' || fontWeight === '700') {
|
||||||
|
charWidth *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
charWidths.push(charWidth);
|
||||||
|
totalWidth += charWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为文字之间添加2px间距(除了最后一个字符)
|
||||||
|
const spacing = 0;
|
||||||
|
const totalSpacing = (text.length > 1) ? (text.length - 1) * spacing : 0;
|
||||||
|
const totalWidthWithSpacing = totalWidth + totalSpacing;
|
||||||
|
|
||||||
|
// 计算文字总弧长和每个字符的角度(正面使用原始半径)
|
||||||
|
const circumference = 2 * Math.PI * radius;
|
||||||
|
const arcLength = totalWidthWithSpacing;
|
||||||
|
const totalAngle = (arcLength / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算起始角度(使文字居中于底部)
|
||||||
|
// 从底部中心开始,顺时针排列,起始角度 = 底部中心 + 总角度的一半
|
||||||
|
const startAngle = Math.PI / 2 + totalAngle / 2;
|
||||||
|
|
||||||
|
// 创建并定位每个字符(逆时针排列,所以角度递减)
|
||||||
|
let currentAngle = startAngle;
|
||||||
|
const charList = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
const charWidth = charWidths[i];
|
||||||
|
|
||||||
|
// 计算字符角度增量(包括字符宽度和间距)
|
||||||
|
// 最后一个字符不需要添加后面的间距
|
||||||
|
const spacingAfter = (i < text.length - 1) ? spacing : 0;
|
||||||
|
const charAngleIncrement = ((charWidth + spacingAfter) / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算字符角度位置(逆时针排列,所以角度递减)
|
||||||
|
// 字符中心位置 = 当前角度 - 字符宽度的一半对应的角度
|
||||||
|
const charWidthAngle = (charWidth / circumference) * 2 * Math.PI;
|
||||||
|
const charAngle = currentAngle - charWidthAngle / 2;
|
||||||
|
|
||||||
|
// 计算字符位置(正面使用原始半径)
|
||||||
|
const x = centerX + radius * Math.cos(charAngle);
|
||||||
|
const y = centerY + radius * Math.sin(charAngle) - fontSize / 3;
|
||||||
|
|
||||||
|
// 计算旋转角度(正面:文字底部向外,顶部朝向圆心)
|
||||||
|
const rotateAngle = charAngle + Math.PI * 1.5;
|
||||||
|
// 转换为度数(用于CSS transform)
|
||||||
|
const rotateDeg = (rotateAngle * 180) / Math.PI;
|
||||||
|
|
||||||
|
charList.push({
|
||||||
|
char: char,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
rotate: rotateAngle,
|
||||||
|
rotateDeg: rotateDeg, // 度数版本
|
||||||
|
fontSize: fontSize,
|
||||||
|
fontWeight: fontWeight,
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新当前角度(逆时针排列,所以角度递减)
|
||||||
|
currentAngle -= charAngleIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return charList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算背面圆形文字中每个字符的位置和样式
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @param {string} options.text - 文字内容
|
||||||
|
* @param {number} options.centerX - 圆心X坐标(相对于容器)
|
||||||
|
* @param {number} options.centerY - 圆心Y坐标(相对于容器)
|
||||||
|
* @param {number} options.radius - 半径
|
||||||
|
* @param {number} options.fontSize - 字体大小
|
||||||
|
* @param {string} options.fontWeight - 字体粗细
|
||||||
|
* @param {string} options.color - 文字颜色
|
||||||
|
* @returns {Array} 字符位置信息数组
|
||||||
|
*/
|
||||||
|
function calculateBackRoundTextChars({ text, centerX, centerY, radius, fontSize, fontWeight, color }) {
|
||||||
|
if (!text) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算字符宽度
|
||||||
|
// 使用更精确的估算方法
|
||||||
|
const charWidths = [];
|
||||||
|
let totalWidth = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
let charWidth;
|
||||||
|
|
||||||
|
// 判断字符类型,使用不同的宽度估算
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(char)) {
|
||||||
|
// 中文字符:宽度约为 fontSize
|
||||||
|
charWidth = fontSize;
|
||||||
|
} else if (/[0-9]/.test(char)) {
|
||||||
|
// 数字:宽度约为 fontSize * 0.6
|
||||||
|
charWidth = fontSize * 0.6;
|
||||||
|
} else if (/[a-zA-Z]/.test(char)) {
|
||||||
|
// 英文字母:宽度约为 fontSize * 0.6(大写)或 0.5(小写)
|
||||||
|
charWidth = /[A-Z]/.test(char) ? fontSize * 0.65 : fontSize * 0.5;
|
||||||
|
} else if (/[\u3000-\u303f\uff00-\uffef]/.test(char)) {
|
||||||
|
// 全角符号:宽度约为 fontSize
|
||||||
|
charWidth = fontSize;
|
||||||
|
} else {
|
||||||
|
// 其他字符(半角符号等):宽度约为 fontSize * 0.4
|
||||||
|
charWidth = fontSize * 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字体是粗体,稍微增加宽度
|
||||||
|
if (fontWeight === 'bold' || fontWeight === '700') {
|
||||||
|
charWidth *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
charWidths.push(charWidth);
|
||||||
|
totalWidth += charWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为文字之间添加2px间距(除了最后一个字符)
|
||||||
|
const spacing = 1;
|
||||||
|
const totalSpacing = (text.length > 1) ? (text.length - 1) * spacing : 0;
|
||||||
|
const totalWidthWithSpacing = totalWidth + totalSpacing;
|
||||||
|
|
||||||
|
// 计算文字总弧长和每个字符的角度
|
||||||
|
// 背面:文字到圆心的距离 = 半径 + 文字高度的一半
|
||||||
|
const adjustedRadius = radius + fontSize / 2;
|
||||||
|
const circumference = 2 * Math.PI * adjustedRadius;
|
||||||
|
const arcLength = totalWidthWithSpacing;
|
||||||
|
const totalAngle = (arcLength / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算起始角度(使文字居中于底部)
|
||||||
|
// 从底部中心开始,顺时针排列,起始角度 = 底部中心 - 总角度的一半
|
||||||
|
const startAngle = Math.PI / 2 - totalAngle / 2;
|
||||||
|
|
||||||
|
// 创建并定位每个字符(顺时针排列,所以角度递增)
|
||||||
|
let currentAngle = startAngle;
|
||||||
|
const charList = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
const charWidth = charWidths[i];
|
||||||
|
|
||||||
|
// 计算字符角度增量(包括字符宽度和间距)
|
||||||
|
// 最后一个字符不需要添加后面的间距
|
||||||
|
const spacingAfter = (i < text.length - 1) ? spacing : 0;
|
||||||
|
const charAngleIncrement = ((charWidth + spacingAfter) / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算字符角度位置(顺时针排列,所以角度递增)
|
||||||
|
// 字符中心位置 = 当前角度 + 字符宽度的一半对应的角度
|
||||||
|
const charWidthAngle = (charWidth / circumference) * 2 * Math.PI;
|
||||||
|
const charAngle = currentAngle + charWidthAngle / 2;
|
||||||
|
|
||||||
|
// 计算字符位置(背面:文字到圆心的距离 = 半径 + 文字高度的一半)
|
||||||
|
const x = centerX + adjustedRadius * Math.cos(charAngle);
|
||||||
|
const y = centerY + adjustedRadius * Math.sin(charAngle);
|
||||||
|
|
||||||
|
// 计算旋转角度(背面:文字底部朝向圆心,顶部向外)
|
||||||
|
const rotateAngle = charAngle + Math.PI * 0.5;
|
||||||
|
// 转换为度数(用于CSS transform)
|
||||||
|
const rotateDeg = (rotateAngle * 180) / Math.PI;
|
||||||
|
|
||||||
|
charList.push({
|
||||||
|
char: char,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
rotate: rotateAngle,
|
||||||
|
rotateDeg: rotateDeg, // 度数版本
|
||||||
|
fontSize: fontSize,
|
||||||
|
fontWeight: fontWeight,
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新当前角度(顺时针排列,所以角度递增)
|
||||||
|
currentAngle += charAngleIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return charList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化圆形文字 Canvas
|
||||||
|
* @param {string} canvasId - Canvas 的 ID 或选择器
|
||||||
|
* @param {Object} config - 配置对象,包含 width 和 height
|
||||||
|
* @returns {Promise} 返回包含 canvas 和 ctx 的对象
|
||||||
|
*/
|
||||||
|
function initRoundTextCanvas(canvasId, config) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const tryInit = () => {
|
||||||
|
// 如果是选择器字符串,去掉 # 或 . 前缀
|
||||||
|
const selector = canvasId.startsWith('#') ? canvasId.slice(1) :
|
||||||
|
canvasId.startsWith('.') ? canvasId.slice(1) : canvasId;
|
||||||
|
|
||||||
|
// 尝试通过 ID 查找
|
||||||
|
let canvasElement: HTMLCanvasElement | null = document.getElementById(selector) as HTMLCanvasElement | null;
|
||||||
|
|
||||||
|
// 如果找不到,尝试通过类名查找(取第一个)
|
||||||
|
if (!canvasElement && canvasId.startsWith('.')) {
|
||||||
|
const elements = document.getElementsByClassName(selector);
|
||||||
|
if (elements.length > 0) {
|
||||||
|
canvasElement = elements[0] as HTMLCanvasElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还是找不到,尝试直接使用 canvasId 作为选择器
|
||||||
|
if (!canvasElement) {
|
||||||
|
canvasElement = document.querySelector(canvasId) as HTMLCanvasElement | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvasElement && canvasElement instanceof HTMLCanvasElement) {
|
||||||
|
const canvas = canvasElement;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('无法获取 Canvas 2D 上下文');
|
||||||
|
}
|
||||||
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
|
// 使用配置中的尺寸,如果没有则使用 Canvas 的实际尺寸或默认值
|
||||||
|
const width = (config && config.width) || canvas.offsetWidth || 700;
|
||||||
|
const height = (config && config.height) || canvas.offsetHeight || 700;
|
||||||
|
|
||||||
|
canvas.width = width * dpr;
|
||||||
|
canvas.height = height * dpr;
|
||||||
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
|
resolve({ canvas, ctx });
|
||||||
|
} else {
|
||||||
|
// 如果 Canvas 还未渲染,延迟重试
|
||||||
|
setTimeout(() => {
|
||||||
|
tryInit();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tryInit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 Canvas 上绘制圆形文字
|
||||||
|
* @param {Object} options - 绘制选项
|
||||||
|
* @param {Object} options.ctx - Canvas 2D 上下文
|
||||||
|
* @param {Object} options.config - 圆形文字配置对象
|
||||||
|
* @param {Function} options.setData - 设置数据的方法
|
||||||
|
* @param {HTMLCanvasElement} options.canvas - Canvas 元素
|
||||||
|
* @param {string} options.imageKey - 图片数据键名,用于存储生成的图片路径
|
||||||
|
* @returns {Promise} 返回生成的图片路径(Data URL)
|
||||||
|
*/
|
||||||
|
function drawRoundTextOnCanvas({ ctx, config, setData, canvas, imageKey = 'roundTextImage' }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!ctx || !config) {
|
||||||
|
console.log('drawRoundTextOnCanvas: ctx 或 config 不存在', { ctx: !!ctx, config: !!config });
|
||||||
|
if (setData) {
|
||||||
|
setData({
|
||||||
|
[imageKey]: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = config.text;
|
||||||
|
if (!text) {
|
||||||
|
console.log('drawRoundTextOnCanvas: 文字为空');
|
||||||
|
// 如果没有文字,清空图片
|
||||||
|
if (setData) {
|
||||||
|
setData({
|
||||||
|
[imageKey]: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('drawRoundTextOnCanvas: 开始绘制文字', { text, config });
|
||||||
|
|
||||||
|
// 使用配置中的宽高
|
||||||
|
const canvasWidth = config.width || 700;
|
||||||
|
const canvasHeight = config.height || 700;
|
||||||
|
// 圆心位置:根据需求,圆心应该在容器的中心
|
||||||
|
// 由于容器就是 Canvas,所以圆心就是 Canvas 的中心
|
||||||
|
const centerX = canvasWidth / 2;
|
||||||
|
const centerY = canvasHeight / 2;
|
||||||
|
const radius = config.radius;
|
||||||
|
const fontSize = config.style.fontSize;
|
||||||
|
const color = config.style.color || '#000000';
|
||||||
|
const fontWeight = config.style.fontWeight || 'normal';
|
||||||
|
|
||||||
|
// 清空画布(注意:Canvas 的实际尺寸已经乘以了 dpr,但绘制时使用的是逻辑尺寸)
|
||||||
|
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
// 设置字体样式
|
||||||
|
ctx.font = `${fontWeight} ${fontSize}px sans-serif`;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle'; // 使用 middle 基线,文字中心对齐,更适合圆形排列
|
||||||
|
|
||||||
|
// 计算每个字符的角度
|
||||||
|
const circumference = 2 * Math.PI * radius;
|
||||||
|
const charWidths = [];
|
||||||
|
let totalWidth = 0;
|
||||||
|
|
||||||
|
// 测量每个字符的宽度
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
const metrics = ctx.measureText(char);
|
||||||
|
const width = metrics.width;
|
||||||
|
charWidths.push(width);
|
||||||
|
totalWidth += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算文字总弧长和每个字符的角度
|
||||||
|
const arcLength = totalWidth;
|
||||||
|
const totalAngle = (arcLength / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算起始角度(使文字居中于底部)
|
||||||
|
// 从底部中心开始,顺时针排列,起始角度 = 底部中心 + 总角度的一半
|
||||||
|
const startAngle = Math.PI / 2 + totalAngle / 2;
|
||||||
|
|
||||||
|
// 创建并定位每个字符(逆时针排列,所以角度递减)
|
||||||
|
let currentAngle = startAngle;
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
const charWidth = charWidths[i];
|
||||||
|
|
||||||
|
// 计算字符角度增量
|
||||||
|
const charAngleIncrement = (charWidth / circumference) * 2 * Math.PI;
|
||||||
|
|
||||||
|
// 计算字符角度位置(逆时针排列,所以角度递减)
|
||||||
|
const charAngle = currentAngle - charAngleIncrement / 2;
|
||||||
|
|
||||||
|
// 计算字符位置
|
||||||
|
const x = centerX + radius * Math.cos(charAngle);
|
||||||
|
const y = centerY + radius * Math.sin(charAngle);
|
||||||
|
|
||||||
|
// 保存上下文
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
// 移动到字符位置
|
||||||
|
ctx.translate(x, y);
|
||||||
|
|
||||||
|
// 旋转文字,使其沿着圆形路径排列
|
||||||
|
// 文字底部向外,顶部朝向圆心,所以旋转角度为字符角度 + π * 1.5
|
||||||
|
ctx.rotate(charAngle + Math.PI * 1.5);
|
||||||
|
|
||||||
|
// 绘制文字(使用 0, 0 作为中心点,因为已经 translate 到字符位置)
|
||||||
|
ctx.fillText(char, 0, 0);
|
||||||
|
|
||||||
|
// 恢复上下文
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// 更新当前角度(逆时针排列,所以角度递减)
|
||||||
|
currentAngle -= charAngleIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 Canvas 转换为图片(Data URL)
|
||||||
|
try {
|
||||||
|
const dataURL = canvas.toDataURL('image/png');
|
||||||
|
if (setData) {
|
||||||
|
setData({
|
||||||
|
[imageKey]: dataURL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve(dataURL);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Canvas 转图片失败:', err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
roundText,
|
||||||
|
initRoundTextCanvas,
|
||||||
|
drawRoundTextOnCanvas,
|
||||||
|
calculateFrontRoundTextChars,
|
||||||
|
calculateBackRoundTextChars
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -27,6 +27,9 @@
|
|||||||
<div class="order-type-item-title" v-if="typeId == 4">
|
<div class="order-type-item-title" v-if="typeId == 4">
|
||||||
{{ toValueWithout("3D卡通手办") }}
|
{{ toValueWithout("3D卡通手办") }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="order-type-item-title" v-if="typeId == 6">
|
||||||
|
{{ toValueWithout("浮雕挂饰") }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="size-title">
|
<div class="size-title">
|
||||||
@@ -76,11 +79,11 @@
|
|||||||
<p style="font-size: 12px; color: #000; margin-left: 16px;">{{ toValueWithout("注:需要先输入手机号才可体验徽章设计") }}</p>
|
<p style="font-size: 12px; color: #000; margin-left: 16px;">{{ toValueWithout("注:需要先输入手机号才可体验徽章设计") }}</p>
|
||||||
<div class="style-box">
|
<div class="style-box">
|
||||||
<div class="style-box-item">
|
<div class="style-box-item">
|
||||||
<div class="style-item-title" :class="{ styleActive: prodId == 7 || prodId == 1 }" @click="styleChange(7)">
|
<div class="style-item-title" :class="{ styleActive: prodId == 7 || prodId == 1 || prodId == 25 }" @click="styleChange(7)">
|
||||||
{{ prop == '3D真人肖像' ? toValueWithout('3D真人肖像') : toValueWithout('人物立体徽章') }}
|
{{ prop == '3D真人肖像' ? toValueWithout('3D真人肖像') : toValueWithout('人物主体') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="style-item-title" :class="{ styleActive: prodId == 8 || prodId == 2 }" @click="styleChange(8)">
|
<div class="style-item-title" :class="{ styleActive: prodId == 8 || prodId == 2 || prodId == 24 }" @click="styleChange(8)">
|
||||||
{{ toValueWithout("宠物立体徽章") }}
|
{{ toValueWithout("宠物主体") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,10 +119,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tag-change-box" v-if="trialCode == false">
|
<div class="tag-change-box" v-if="trialCode == false">
|
||||||
<div class="tag-change-item">
|
<div class="tag-change-item">
|
||||||
<div class="tag-change-item-title" :class="{ tagActive: prodId == 7 || prodId == 1 }" @click="prodChange(7)">
|
<div class="tag-change-item-title" :class="{ tagActive: prodId == 7 || prodId == 1 || prodId == 25 }" @click="prodChange(7)">
|
||||||
{{ toValueWithout("人物主体") }}
|
{{ toValueWithout("人物主体") }}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-change-item-title" :class="{ tagActive: prodId == 8 || prodId == 19 }" @click="prodChange(8)">
|
<div class="tag-change-item-title" :class="{ tagActive: prodId == 8 || prodId == 19 || prodId == 24 }" @click="prodChange(8)">
|
||||||
{{ toValueWithout("宠物主体") }}
|
{{ toValueWithout("宠物主体") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -194,8 +197,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="typeId != 4">
|
<div v-if="typeId != 4">
|
||||||
<div style="font-size: 12px; color: red; margin-top: 16px; text-align: center;" v-if="prodId == 7 || prodId == 1">{{ toValueWithout("温馨提示:请上传只有") }}{{ prop == '3D真人肖像' ? '1' : '1-3' }}{{ toValueWithout("人的照片") }}</div>
|
<div style="font-size: 12px; color: red; margin-top: 16px; text-align: center;" v-if="prodId == 7 || prodId == 1 || prodId == 25">{{ toValueWithout("温馨提示:请上传只有") }}{{ prop == '3D真人肖像' ? '1' : '1-3' }}{{ toValueWithout("人的照片") }}</div>
|
||||||
<div style="font-size: 12px; color: red; margin-top: 16px; text-align: center;" v-if="prodId == 8 || prodId == 2">{{ toValueWithout("温馨提示:请上传只有") }}1-3{{ toValueWithout("只宠物的照片") }}</div>
|
<div style="font-size: 12px; color: red; margin-top: 16px; text-align: center;" v-if="prodId == 8 || prodId == 2 || prodId == 24">{{ toValueWithout("温馨提示:请上传只有") }}1-3{{ toValueWithout("只宠物的照片") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 120px;"></div>
|
<div style="height: 120px;"></div>
|
||||||
<div class="design-action-bar">
|
<div class="design-action-bar">
|
||||||
@@ -356,11 +359,9 @@ const typeId = ref(0)
|
|||||||
const getOrderStat = () => {
|
const getOrderStat = () => {
|
||||||
badgeApi.getOrderStat({}).then((res: any) => {
|
badgeApi.getOrderStat({}).then((res: any) => {
|
||||||
orderStat.value = res
|
orderStat.value = res
|
||||||
|
prodId.value = getDefaultProdId(res)
|
||||||
if (res.type_id == 4) {
|
if (res.type_id == 4) {
|
||||||
prodId.value = res.prod_id == 1 ? 1 : 19
|
|
||||||
getKindList()
|
getKindList()
|
||||||
} else {
|
|
||||||
prodId.value = res.type_id == 3 ? 8 : res.prod_id
|
|
||||||
}
|
}
|
||||||
prop.value = res.prop
|
prop.value = res.prop
|
||||||
typeId.value = res.type_id
|
typeId.value = res.type_id
|
||||||
@@ -368,10 +369,22 @@ const getOrderStat = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDefaultProdId = (res: any) => {
|
||||||
|
if (res.type_id == 4) {
|
||||||
|
return res.prod_id == 7 || res.prod_id == 1 ? 1 : 19
|
||||||
|
} else if (res.type_id == 6) {
|
||||||
|
return res.prod_id == 7 || res.prod_id == 25 ? 25 : 24
|
||||||
|
} else if (res.type_id == 3) {
|
||||||
|
return res.prod_id == 7 ? 7 : 8
|
||||||
|
} else {
|
||||||
|
return res.prod_id == 7 ? 7 : 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const kindList = ref([])
|
const kindList = ref([])
|
||||||
const getKindList = () => {
|
const getKindList = () => {
|
||||||
badgeApi.getKindList({
|
badgeApi.getKindList({
|
||||||
support_subject: prodId.value == 19 ? 2 : 1,
|
support_subject: getSupportSubject(),
|
||||||
type_id: 4
|
type_id: 4
|
||||||
}).then((res: any) => {
|
}).then((res: any) => {
|
||||||
kindList.value = res.list
|
kindList.value = res.list
|
||||||
@@ -380,8 +393,15 @@ const getKindList = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSupportSubject = () => {
|
||||||
|
if (prodId.value == 19 || prodId.value == 24 || prodId.value == 8) {
|
||||||
|
return 2
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const prodChange = (id: number) => {
|
const prodChange = (id: number) => {
|
||||||
console.log('prodChange', id)
|
|
||||||
if (typeId.value == 3 && id == 7) {
|
if (typeId.value == 3 && id == 7) {
|
||||||
showToast(toValueWithout('人物3D冰箱贴暂未开放'))
|
showToast(toValueWithout('人物3D冰箱贴暂未开放'))
|
||||||
return
|
return
|
||||||
@@ -396,7 +416,14 @@ const prodChange = (id: number) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prodId.value = typeId.value == 4 ? id == 8 ? 19 : 1 : id == 7 ? 7 : 8
|
if (typeId.value == 6) {
|
||||||
|
prodId.value = id == 8 ? 24 : 25
|
||||||
|
} else if (typeId.value == 4) {
|
||||||
|
prodId.value = id == 8 ? 19 : 1
|
||||||
|
} else {
|
||||||
|
prodId.value = id == 7 ? 7 : 8
|
||||||
|
}
|
||||||
|
console.log('prodChange', prodId.value)
|
||||||
picture.value = null
|
picture.value = null
|
||||||
kindId.value = 0
|
kindId.value = 0
|
||||||
}
|
}
|
||||||
@@ -489,7 +516,7 @@ function getbase64Data(data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
imgurl.value = blob;
|
imgurl.value = blob;
|
||||||
if (prodId.value == 7 || prodId.value == 1) {
|
if (prodId.value == 7 || prodId.value == 1 || prodId.value == 25) {
|
||||||
getUploadUrl()
|
getUploadUrl()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +710,7 @@ const getPid = async () => {
|
|||||||
try {
|
try {
|
||||||
const uploadTasks = [
|
const uploadTasks = [
|
||||||
sendToOss(imgurl.value, res.url),
|
sendToOss(imgurl.value, res.url),
|
||||||
sendToOss(imgurl.value, res.oss_url)
|
// sendToOss(imgurl.value, res.oss_url)
|
||||||
]
|
]
|
||||||
await Promise.all(uploadTasks)
|
await Promise.all(uploadTasks)
|
||||||
if (typeId.value == 4 && prodId.value == 19 && referPicture.value.length > 0) {
|
if (typeId.value == 4 && prodId.value == 19 && referPicture.value.length > 0) {
|
||||||
@@ -815,7 +842,7 @@ const getSundryList = () => {
|
|||||||
badgeApi.getSundryList({
|
badgeApi.getSundryList({
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 6,
|
size: 6,
|
||||||
group: prodId.value == 7 || prodId.value == 1 ? 'gp' : 'pet',
|
group: prodId.value == 7 || prodId.value == 1 || prodId.value == 25 ? 'gp' : 'pet',
|
||||||
}).then((res: any) => {
|
}).then((res: any) => {
|
||||||
const data = res || []
|
const data = res || []
|
||||||
sundryList.value = data.list
|
sundryList.value = data.list
|
||||||
@@ -871,9 +898,10 @@ function Encrypt(word: string): string {
|
|||||||
return encrypted.ciphertext.toString();
|
return encrypted.ciphertext.toString();
|
||||||
}
|
}
|
||||||
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
||||||
localStorage.remove('code')
|
// code 的清除已在 main.ts 中统一处理,这里只清除 userId(如果需要)
|
||||||
localStorage.remove('trialCode')
|
// 注意:刷新时不应该清除 userId,只有关闭时才清除
|
||||||
localStorage.remove('userId')
|
// 由于无法精确区分刷新和关闭,这里保留清除逻辑,但主要依赖 main.ts 的处理
|
||||||
|
// localStorage.remove('userId')
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSundryList()
|
getSundryList()
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
<div class="order-status">
|
<div class="order-status">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
<!-- <img v-if="shapeImage" class="avatar-back-img" :src="shapeImage" alt=""> -->
|
<!-- <img v-if="shapeImage" class="avatar-back-img" :src="shapeImage" alt=""> -->
|
||||||
<div class="avatar-front-box">
|
<div class="avatar-front-box" v-if="order.type_id != 6">
|
||||||
<img class="avatar-font-img" :src="imageUrl" :alt="order.kind_name" />
|
<img class="avatar-font-img" :src="imageUrl" :alt="order.kind_name" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="avatar-front-box-double" v-if="order.type_id == 6">
|
||||||
|
<img class="avatar-font-img" :src="imageUrl" />
|
||||||
|
<img class="avatar-font-img" :src="backImageUrl" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-info">
|
<div class="status-info">
|
||||||
@@ -45,12 +49,13 @@
|
|||||||
<span class="product-value" v-if="order.type_id == 2">{{ order.prod_id == 7 ? toValueWithout('人物浮雕相框') : toValueWithout('宠物浮雕相框') }}</span>
|
<span class="product-value" v-if="order.type_id == 2">{{ order.prod_id == 7 ? toValueWithout('人物浮雕相框') : toValueWithout('宠物浮雕相框') }}</span>
|
||||||
<span class="product-value" v-if="order.type_id == 3">{{ order.prod_id == 7 ? toValueWithout('人物冰箱贴') : toValueWithout('宠物冰箱贴') }}</span>
|
<span class="product-value" v-if="order.type_id == 3">{{ order.prod_id == 7 ? toValueWithout('人物冰箱贴') : toValueWithout('宠物冰箱贴') }}</span>
|
||||||
<span class="product-value" v-if="order.type_id == 4">{{ order.prod_id == 1 ? toValueWithout('人物卡通手办') : toValueWithout('宠物卡通手办') }}</span>
|
<span class="product-value" v-if="order.type_id == 4">{{ order.prod_id == 1 ? toValueWithout('人物卡通手办') : toValueWithout('宠物卡通手办') }}</span>
|
||||||
|
<span class="product-value" v-if="order.type_id == 6">{{ order.prod_id == 25 ? toValueWithout('人物浮雕挂饰') : toValueWithout('宠物浮雕挂饰') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="product-row">
|
<div class="product-row">
|
||||||
<span class="product-label">{{ toValueWithout("尺寸") }}</span>
|
<span class="product-label">{{ toValueWithout("尺寸") }}</span>
|
||||||
<span class="product-value">{{order.model_size}}</span>
|
<span class="product-value">{{order.model_size}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="product-row" v-if="shape_name">
|
<div class="product-row" v-if="shape_name && order.type_id != 6">
|
||||||
<span class="product-label">{{ toValueWithout("形状") }}</span>
|
<span class="product-label">{{ toValueWithout("形状") }}</span>
|
||||||
<span class="product-value">{{ toValueWithout(shape_name) }}</span>
|
<span class="product-value">{{ toValueWithout(shape_name) }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,20 +65,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div style="padding: 0 16px;">
|
|
||||||
<van-divider />
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="address-info" style="padding: 0 16px;">
|
|
||||||
<div class="address-title">收货信息</div>
|
|
||||||
<div class="address-row">
|
|
||||||
<img class="address-img" src="@/assets/badge/location.png" alt="" >
|
|
||||||
<span class="address-name">张先生</span>
|
|
||||||
<span class="address-phone">136****0731</span>
|
|
||||||
</div>
|
|
||||||
<div class="address-detail">
|
|
||||||
详细地址详细地址详细地址详细地址详细地址详细地址
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<div style="padding: 0 16px;">
|
<div style="padding: 0 16px;">
|
||||||
<van-divider />
|
<van-divider />
|
||||||
</div>
|
</div>
|
||||||
@@ -99,6 +90,7 @@ import * as badgeApi from '@/api/badge';
|
|||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { toValueWithout } from '@/lang/utils';
|
import { toValueWithout } from '@/lang/utils';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const order = ref({})
|
const order = ref({})
|
||||||
const id = ref(0)
|
const id = ref(0)
|
||||||
@@ -109,17 +101,24 @@ onMounted(() => {
|
|||||||
|
|
||||||
const shapeImage = ref('')
|
const shapeImage = ref('')
|
||||||
const imageUrl = ref('')
|
const imageUrl = ref('')
|
||||||
|
const backImageUrl = ref('')
|
||||||
const getStyle = ref('')
|
const getStyle = ref('')
|
||||||
const shape_name = ref('')
|
const shape_name = ref('')
|
||||||
|
const imageList = ref([])
|
||||||
|
|
||||||
function getOrderDetail() {
|
function getOrderDetail() {
|
||||||
badgeApi.getOrderDetail({
|
badgeApi.getOrderDetail({
|
||||||
id: id.value,
|
id: id.value,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
order.value = res
|
order.value = res;
|
||||||
imageUrl.value = res.path
|
imageUrl.value = res.path;
|
||||||
shapeImage.value = res.shape_details.frame_path
|
backImageUrl.value = res.back_path;
|
||||||
shape_name.value = res.shape_details.name
|
shapeImage.value = res.shape_details.frame_path;
|
||||||
|
shape_name.value = res.shape_details.name;
|
||||||
|
imageList.value = [
|
||||||
|
res.path,
|
||||||
|
res.back_path,
|
||||||
|
];
|
||||||
// if (res.shape_details) {
|
// if (res.shape_details) {
|
||||||
// ImageShow(res.shape_details)
|
// ImageShow(res.shape_details)
|
||||||
// }
|
// }
|
||||||
@@ -209,6 +208,14 @@ function getStatus(status: number) {
|
|||||||
max-height: 320px;
|
max-height: 320px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
.avatar-front-box-double {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
.progress-image-item-shadow {
|
.progress-image-item-shadow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -35,6 +35,18 @@
|
|||||||
<!-- <div class="box1-round-shadow" v-if="shapeId == 1 && shapeImage == ''" style="width: 320px;height: 320px;"></div> -->
|
<!-- <div class="box1-round-shadow" v-if="shapeId == 1 && shapeImage == ''" style="width: 320px;height: 320px;"></div> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="shape-text" :style="shapeTextStyle" v-if="shapeText">{{shapeText}}</div>
|
<div class="shape-text" :style="shapeTextStyle" v-if="shapeText">{{shapeText}}</div>
|
||||||
|
<div class="shape-text" :style="topTextStyle" v-if="topText">{{topText}}</div>
|
||||||
|
<div class="shape-text" :style="bottomTextStyle" v-if="bottomText">{{bottomText}}</div>
|
||||||
|
<div v-if="badgeTypeId == 6 && axisz == 1 && roundTextConfigFront.width && roundTextConfigFront.charList" class="roundText font-chinese" :style="`left: ${roundTextConfigFront.x}px;top: ${roundTextConfigFront.y}px;width: ${roundTextConfigFront.width}px;height: ${roundTextConfigFront.height}px;position: absolute;pointer-events: none;z-index: 10;`">
|
||||||
|
<div v-for="item in roundTextConfigFront.charList" :key="item.char" class="round-text-char" :style="`position: absolute;left: ${item.x}px;top: ${item.y}px;font-size: ${item.fontSize}px;font-weight: ${item.fontWeight};color: ${item.color};transform: translate(-50%, -50%) rotate(${item.rotateDeg}deg);transform-origin: center;white-space: nowrap;`">
|
||||||
|
<span>{{item.char}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="badgeTypeId == 6 && axisz == -1 && roundTextConfigBack.width && roundTextConfigBack.charList" class="roundText font-chinese" :style="`left: ${roundTextConfigBack.x}px;top: ${roundTextConfigBack.y}px;width: ${roundTextConfigBack.width}px;height: ${roundTextConfigBack.height}px;position: absolute;pointer-events: none;z-index: 10;`">
|
||||||
|
<div v-for="(item, index) in roundTextConfigBack.charList" :key="index" class="round-text-char" :style="`position: absolute;left: ${item.x}px;top: ${item.y}px;font-size: ${item.fontSize}px;font-weight: ${item.fontWeight};color: ${item.color};transform: translate(-50%, -50%) rotate(${item.rotateDeg}deg);transform-origin: center;white-space: nowrap;`">
|
||||||
|
<span>{{item.char}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-list-box" v-if="imageList.length > 1">
|
<div class="image-list-box" v-if="imageList.length > 1">
|
||||||
@@ -54,6 +66,7 @@
|
|||||||
<div class="info-title" v-if="typeId == 2">{{ prodId == 7 ? toValueWithout('人物浮雕相框') : toValueWithout('宠物浮雕相框') }}</div>
|
<div class="info-title" v-if="typeId == 2">{{ prodId == 7 ? toValueWithout('人物浮雕相框') : toValueWithout('宠物浮雕相框') }}</div>
|
||||||
<div class="info-title" v-if="typeId == 3">{{ prodId == 7 ? toValueWithout('人物3D冰箱贴') : toValueWithout('宠物3D冰箱贴') }}</div>
|
<div class="info-title" v-if="typeId == 3">{{ prodId == 7 ? toValueWithout('人物3D冰箱贴') : toValueWithout('宠物3D冰箱贴') }}</div>
|
||||||
<div class="info-title" v-if="typeId == 4">{{ prodId == 1 ? toValueWithout('人物卡通手办') : toValueWithout('宠物卡通手办') }}</div>
|
<div class="info-title" v-if="typeId == 4">{{ prodId == 1 ? toValueWithout('人物卡通手办') : toValueWithout('宠物卡通手办') }}</div>
|
||||||
|
<div class="info-title" v-if="typeId == 6">{{ prodId == 25 ? toValueWithout('人物浮雕挂饰') : toValueWithout('宠物浮雕挂饰') }}</div>
|
||||||
<div class="info-content">{{ toValueWithout("产品类型") }}</div>
|
<div class="info-content">{{ toValueWithout("产品类型") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
@@ -80,10 +93,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</block>
|
</block>
|
||||||
</div>
|
</div>
|
||||||
<div class="shape-box-input" v-if="imageUrl && custom_switch == 1">
|
<div class="shape-box-input" v-if="imageUrl && custom_switch == 1 && typeId == 2">
|
||||||
|
<div v-if="shapeId == 27 || shapeId == 28">
|
||||||
|
<input class="shape-box-input-text" type="text" :placeholder="toValueWithout('请输入顶部文字')" v-model="topText" @change="changeTopText" @input="changeTopText" @onBlur="changeTopText" />
|
||||||
|
<input class="shape-box-input-text" type="text" :placeholder="toValueWithout('请输入底部文字')" style="margin-top: 10px;" v-model="bottomText" @change="changeBottomText" @input="changeBottomText" @onBlur="changeBottomText" />
|
||||||
|
</div>
|
||||||
|
<div v-else style="width: 100%;">
|
||||||
<input class="shape-box-input-text" type="text" :placeholder="toValueWithout('请输入文字')" v-model="shapeText" @change="changeShapeText" @input="changeShapeText" @onBlur="changeShapeText" />
|
<input class="shape-box-input-text" type="text" :placeholder="toValueWithout('请输入文字')" v-model="shapeText" @change="changeShapeText" @input="changeShapeText" @onBlur="changeShapeText" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="shape-box-input-box" v-if="imageUrl && custom_switch == 1 && typeId == 6">
|
||||||
|
<input class="shape-box-input-text" v-if="axisz == 1" type="text" :placeholder="toValueWithout('请输入正面文字')" v-model="frontText" @change="changeFrontText" @input="changeFrontText" @onBlur="changeFrontText" />
|
||||||
|
<input class="shape-box-input-text" v-if="axisz == -1" type="text" :placeholder="toValueWithout('请输入背面文字')" v-model="backText" @change="changeBackText" @input="changeBackText" @onBlur="changeBackText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- <div class="info-desc">
|
<!-- <div class="info-desc">
|
||||||
下单后预计需要7天完成生产发货。<br>
|
下单后预计需要7天完成生产发货。<br>
|
||||||
实物3D打印色彩与图片有合理色差,如对产品有疑问,可以联系客服咨询。
|
实物3D打印色彩与图片有合理色差,如对产品有疑问,可以联系客服咨询。
|
||||||
@@ -103,15 +126,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</block>
|
</block>
|
||||||
<!-- <block v-if="orderStat.use_type == 2">
|
|
||||||
<div class="order-item">
|
|
||||||
<span class="order-size">{{ item.size }}</span>
|
|
||||||
<span class="order-free">({{ toValueWithout("剩余兑换") }}:{{ item.remaining }})</span>
|
|
||||||
<div class="order-ctrl">
|
|
||||||
<van-stepper v-model="item.count" :min="0" :max="item.remaining" @change="changeValue" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</block> -->
|
|
||||||
</block>
|
</block>
|
||||||
</div>
|
</div>
|
||||||
<div class="address-box" v-if="orderStat.use_type == 2 && imageUrl && code == 'ACAYOAAC3PFCPO3CD6DVM1'">
|
<div class="address-box" v-if="orderStat.use_type == 2 && imageUrl && code == 'ACAYOAAC3PFCPO3CD6DVM1'">
|
||||||
@@ -193,13 +207,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, onUnmounted } from 'vue';
|
import { onMounted, ref, onUnmounted, nextTick } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { showSuccessToast, showToast, showNotify, showConfirmDialog, AddressEdit, Area } from 'vant';
|
import { showSuccessToast, showToast, showNotify, showConfirmDialog, AddressEdit, Area } from 'vant';
|
||||||
import { localStorage } from '@/utils/local-storage'
|
import { localStorage } from '@/utils/local-storage'
|
||||||
import * as badgeApi from '@/api/badge'
|
import * as badgeApi from '@/api/badge'
|
||||||
import { areaList } from '@/utils/area'
|
import { areaList } from '@/utils/area'
|
||||||
import { toValueWithout } from '@/lang/utils'
|
import { toValueWithout } from '@/lang/utils'
|
||||||
|
import { createTextPosition } from '@/utils/textHelper'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const value = ref(0);
|
const value = ref(0);
|
||||||
const showReload = ref(false)
|
const showReload = ref(false)
|
||||||
@@ -209,6 +225,36 @@ const currentIndex = ref(0)
|
|||||||
const imageList = ref([])
|
const imageList = ref([])
|
||||||
const imageWater = ref('')
|
const imageWater = ref('')
|
||||||
|
|
||||||
|
const frontText = ref('')
|
||||||
|
const backText = ref('')
|
||||||
|
const frontKey = ref(1)
|
||||||
|
|
||||||
|
// 圆形文字配置
|
||||||
|
const roundTextConfigFront = ref({})
|
||||||
|
const roundTextConfigBack = ref({})
|
||||||
|
const cachedImageInfo = ref(null)
|
||||||
|
const imageInfo = ref(null)
|
||||||
|
const badgeTypeId = ref(0)
|
||||||
|
const axisz = ref(1)
|
||||||
|
|
||||||
|
// 创建适配 Vue 的 textHelper 实例
|
||||||
|
const textHelper = createTextPosition({
|
||||||
|
setData: (data: any) => {
|
||||||
|
// 适配 Vue 的 setData,直接更新 ref 值
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
if (key === 'roundTextConfigFront') {
|
||||||
|
roundTextConfigFront.value = data[key]
|
||||||
|
} else if (key === 'roundTextConfigBack') {
|
||||||
|
roundTextConfigBack.value = data[key]
|
||||||
|
} else if (key === 'cachedImageInfo') {
|
||||||
|
cachedImageInfo.value = data[key]
|
||||||
|
} else if (key === 'imageInfo') {
|
||||||
|
imageInfo.value = data[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const code = localStorage.get('code')
|
const code = localStorage.get('code')
|
||||||
|
|
||||||
function compare() {
|
function compare() {
|
||||||
@@ -497,7 +543,12 @@ const confirm = () => {
|
|||||||
products: sizeList.value,
|
products: sizeList.value,
|
||||||
prod_id: prodId.value,
|
prod_id: prodId.value,
|
||||||
shape_id: shapeId.value,
|
shape_id: shapeId.value,
|
||||||
custom_text: shapeText.value
|
custom_text: shapeText.value ? shapeText.value : frontText.value,
|
||||||
|
back_text: backText.value ? backText.value : '',
|
||||||
|
shape_ids: shapeIds.value,
|
||||||
|
type_id: typeId.value,
|
||||||
|
up_text: topText.value ? topText.value : '',
|
||||||
|
down_text: bottomText.value ? bottomText.value : '',
|
||||||
}
|
}
|
||||||
if (orderStat.value.use_type == 2) {
|
if (orderStat.value.use_type == 2) {
|
||||||
parms.province_id = province_id.value
|
parms.province_id = province_id.value
|
||||||
@@ -545,14 +596,17 @@ const getPosition = () => {
|
|||||||
badgeApi.composite({
|
badgeApi.composite({
|
||||||
pid: pid.value,
|
pid: pid.value,
|
||||||
num: imgKey.value,
|
num: imgKey.value,
|
||||||
custom_text: shapeText.value,
|
custom_text: shapeText.value ? shapeText.value : frontSaveText.value,
|
||||||
|
back_text: backSaveText.value ? backSaveText.value : '',
|
||||||
shape_id: shapeId.value,
|
shape_id: shapeId.value,
|
||||||
|
type_id: typeId.value,
|
||||||
|
shape_ids: shapeIds.value,
|
||||||
|
up_text: topText.value ? topText.value : '',
|
||||||
|
down_text: bottomText.value ? bottomText.value : '',
|
||||||
}).then((res: any) => {
|
}).then((res: any) => {
|
||||||
console.log('getPosition', res)
|
console.log('getPosition', res)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log('getPosition', err)
|
console.log('getPosition', err)
|
||||||
}).finally((err) => {
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,6 +751,7 @@ const getImageList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shapeId = ref(0)
|
const shapeId = ref(0)
|
||||||
|
const shapeIds = ref([])
|
||||||
const shapeImage = ref('')
|
const shapeImage = ref('')
|
||||||
|
|
||||||
const shapeList = ref([])
|
const shapeList = ref([])
|
||||||
@@ -714,13 +769,35 @@ const getShapeList = () => {
|
|||||||
custom_switch.value = res.list[0]?.custom_switch
|
custom_switch.value = res.list[0]?.custom_switch
|
||||||
limitCount.value = res.list[0]?.text_limit_max
|
limitCount.value = res.list[0]?.text_limit_max
|
||||||
ImageShow(res.list[0])
|
ImageShow(res.list[0])
|
||||||
if (custom_switch.value == 1) {
|
if (custom_switch.value == 1 && typeId.value == 2) {
|
||||||
|
shapeText.value = ''
|
||||||
|
topText.value = ''
|
||||||
|
bottomText.value = ''
|
||||||
|
topTextStyle.value = ''
|
||||||
|
bottomTextStyle.value = ''
|
||||||
|
shapeTextStyle.value = ''
|
||||||
textShow(res.list[0])
|
textShow(res.list[0])
|
||||||
}
|
}
|
||||||
|
if (custom_switch.value == 1 && typeId.value == 6) {
|
||||||
|
shapeIds.value = res.list.map((item: any) => item.id)
|
||||||
|
// 根据第一个形状的 id 设置 axisz
|
||||||
|
if (res.list[0].axisz == 1) {
|
||||||
|
axisz.value = 1
|
||||||
|
frontKey.value = 1
|
||||||
|
frontTextShow(res.list[0], true)
|
||||||
|
} else if (res.list[0].axisz == -1) {
|
||||||
|
axisz.value = -1
|
||||||
|
frontKey.value = 2
|
||||||
|
frontTextShow(res.list[0], false)
|
||||||
|
} else {
|
||||||
|
// 默认显示正面
|
||||||
|
axisz.value = 1
|
||||||
|
frontKey.value = 1
|
||||||
|
frontTextShow(res.list[0], true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log('getShapeList', err)
|
console.log('getShapeList', err)
|
||||||
}).finally((err) => {
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,7 +819,9 @@ const ImageShow = (item: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shapeTextStyle = ref('')
|
const shapeTextStyle = ref('')
|
||||||
const textShow = (item: any) => {
|
const topTextStyle = ref('')
|
||||||
|
const bottomTextStyle = ref('')
|
||||||
|
const textShow = (item: any, type: string = 'shape') => {
|
||||||
const scale = 0.6;
|
const scale = 0.6;
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.src = item.frame_path
|
img.src = item.frame_path
|
||||||
@@ -755,28 +834,226 @@ const textShow = (item: any) => {
|
|||||||
const path_width = item.text_width * ratioWidth * scale;
|
const path_width = item.text_width * ratioWidth * scale;
|
||||||
const path_height = item.text_height * ratioHeight * scale;
|
const path_height = item.text_height * ratioHeight * scale;
|
||||||
const text_size = item.font_size * Math.min(ratioWidth * scale, ratioHeight * scale);
|
const text_size = item.font_size * Math.min(ratioWidth * scale, ratioHeight * scale);
|
||||||
|
if (type == 'shape') {
|
||||||
shapeTextStyle.value = `left: ${x}px;top: ${y}px;width: ${path_width}px;height: ${path_height}px;text-align: center;line-height: ${path_height}px;font-size: ${text_size}px;color: ${item.font_color};font-weight: ${item.font_weight};`
|
shapeTextStyle.value = `left: ${x}px;top: ${y}px;width: ${path_width}px;height: ${path_height}px;text-align: center;line-height: ${path_height}px;font-size: ${text_size}px;color: ${item.font_color};font-weight: ${item.font_weight};`
|
||||||
|
} else if (type == 'top') {
|
||||||
|
topTextStyle.value = `left: ${x}px;top: ${y}px;width: ${path_width}px;height: ${path_height}px;text-align: center;line-height: ${path_height}px;font-size: ${text_size}px;color: ${item.font_color};font-weight: ${item.font_weight};`
|
||||||
|
} else if (type == 'bottom') {
|
||||||
|
bottomTextStyle.value = `left: ${x}px;top: ${y}px;width: ${path_width}px;height: ${path_height}px;text-align: center;line-height: ${path_height}px;font-size: ${text_size}px;color: ${item.font_color};font-weight: ${item.font_weight};`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const frontTextStyle = ref('')
|
||||||
|
const backTextStyle = ref('')
|
||||||
|
const frontTextShow = (event: any, isFront: boolean | null = null) => {
|
||||||
|
// 如果没有传递 isFront,从 frontKey 获取
|
||||||
|
if (isFront === null) {
|
||||||
|
isFront = frontKey.value == 1;
|
||||||
|
}
|
||||||
|
// 确保 isFront 是布尔值
|
||||||
|
isFront = Boolean(isFront);
|
||||||
|
|
||||||
|
// 根据正面/背面设置不同的参数
|
||||||
|
let config: any = {};
|
||||||
|
if (isFront) {
|
||||||
|
// 正面文字参数(使用硬编码值或从 event 获取)
|
||||||
|
config = {
|
||||||
|
x: event.front_text_axisx || 163,
|
||||||
|
y: event.front_text_axisy || 222,
|
||||||
|
width: event.front_text_width || 700,
|
||||||
|
height: event.front_text_height || 700,
|
||||||
|
radius: event.front_text_radius || 350,
|
||||||
|
fontSize: event.front_font_size || 72,
|
||||||
|
maxLength: 18
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 背面文字参数(使用 event 中的背面专用参数,完全独立于正面参数)
|
||||||
|
// 优先使用 back_ 开头的参数,如果没有则使用默认值
|
||||||
|
config = {
|
||||||
|
x: event.back_text_axisx !== undefined ? event.back_text_axisx : 238,
|
||||||
|
y: event.back_text_axisy !== undefined ? event.back_text_axisy : 297,
|
||||||
|
width: event.back_text_width !== undefined ? event.back_text_width : 550,
|
||||||
|
height: event.back_text_height !== undefined ? event.back_text_height : 550,
|
||||||
|
radius: event.back_text_radius !== undefined ? event.back_text_radius : 275,
|
||||||
|
fontSize: event.back_font_size !== undefined ? event.back_font_size : 60,
|
||||||
|
maxLength: 56
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = isFront ? frontText.value : backText.value;
|
||||||
|
|
||||||
|
// roundText 会计算字符位置并更新到 ref 中,Vue 会自动渲染
|
||||||
|
// 根据正面/背面存储到不同的配置键
|
||||||
|
const bindKey = isFront ? 'roundTextConfigFront' : 'roundTextConfigBack';
|
||||||
|
|
||||||
|
// 使用缓存的图片信息,避免重复获取,实现实时显示
|
||||||
|
const shapeImagePath = event.frame_path || shapeImage.value;
|
||||||
|
const cachedInfo = cachedImageInfo.value;
|
||||||
|
|
||||||
|
// 如果缓存不存在或图片路径改变,需要重新获取
|
||||||
|
const needFetchImage = !cachedInfo || cachedInfo.src !== shapeImagePath;
|
||||||
|
|
||||||
|
// 如果已有缓存,直接使用缓存计算(实时显示)
|
||||||
|
if (!needFetchImage && cachedInfo) {
|
||||||
|
textHelper.roundText({
|
||||||
|
shapeImage: shapeImagePath,
|
||||||
|
x: config.x,
|
||||||
|
y: config.y,
|
||||||
|
width: config.width,
|
||||||
|
height: config.height,
|
||||||
|
radius: config.radius,
|
||||||
|
text: text,
|
||||||
|
isFront: isFront,
|
||||||
|
style: {
|
||||||
|
fontSize: config.fontSize,
|
||||||
|
color: event.font_color || '#000000',
|
||||||
|
fontWeight: event.font_weight || 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
bindKey: bindKey,
|
||||||
|
cachedImageInfo: cachedInfo // 传入缓存的图片信息
|
||||||
|
});
|
||||||
|
|
||||||
|
// 立即更新显示(因为使用了缓存,计算是同步的)
|
||||||
|
nextTick(() => {
|
||||||
|
const currentConfig = isFront ? roundTextConfigFront.value : roundTextConfigBack.value;
|
||||||
|
if (frontKey.value == (isFront ? 1 : 2) && currentConfig && currentConfig.charList) {
|
||||||
|
// Vue 会自动响应式更新,无需手动 setData
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 需要获取图片信息
|
||||||
|
textHelper.roundText({
|
||||||
|
shapeImage: shapeImagePath,
|
||||||
|
x: config.x,
|
||||||
|
y: config.y,
|
||||||
|
width: config.width,
|
||||||
|
height: config.height,
|
||||||
|
radius: config.radius,
|
||||||
|
text: text,
|
||||||
|
isFront: isFront,
|
||||||
|
style: {
|
||||||
|
fontSize: config.fontSize,
|
||||||
|
color: event.font_color || '#000000',
|
||||||
|
fontWeight: event.font_weight || 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
bindKey: bindKey,
|
||||||
|
onImageInfoCached: (imageInfo: any) => {
|
||||||
|
// 缓存图片信息,下次输入时可以直接使用
|
||||||
|
cachedImageInfo.value = imageInfo;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟更新当前显示的配置(根据 frontKey),确保数据已更新
|
||||||
|
const checkAndUpdate = (attempts = 0) => {
|
||||||
|
if (attempts > 10) return; // 最多尝试10次,避免无限循环
|
||||||
|
|
||||||
|
const currentConfig = isFront ? roundTextConfigFront.value : roundTextConfigBack.value;
|
||||||
|
// 如果当前显示的是对应的面,配置已自动更新(Vue 响应式)
|
||||||
|
if (frontKey.value == (isFront ? 1 : 2)) {
|
||||||
|
if (currentConfig && currentConfig.charList && currentConfig.charList.length > 0) {
|
||||||
|
// Vue 会自动响应式更新,无需手动 setData
|
||||||
|
} else if (attempts < 10) {
|
||||||
|
// 如果配置还没有更新,继续检查
|
||||||
|
setTimeout(() => {
|
||||||
|
checkAndUpdate(attempts + 1);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 立即开始检查
|
||||||
|
setTimeout(() => {
|
||||||
|
checkAndUpdate(0);
|
||||||
|
}, 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const shapeText = ref('')
|
const shapeText = ref('')
|
||||||
const changeShapeText = (e: any) => {
|
const changeShapeText = (e: any) => {
|
||||||
console.log('changeShapeText', e.target.value)
|
console.log('changeShapeText', e.target.value)
|
||||||
// if (!validateInput(e.target.value)) {
|
if (!validateInput(e.target.value)) {
|
||||||
// showToast('输入的文字不能包含特殊符号!')
|
showToast('输入的文字不能包含特殊符号!')
|
||||||
// shapeText.value = e.target.value.replace(/[^a-zA-Z0-9\u4e00-\u9fa5\s]/g, '');
|
shapeText.value = e.target.value.replace(/[^a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]/g, '');
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
handleInput(e.target.value)
|
handleInput(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const topText = ref('')
|
||||||
|
const changeTopText = (e: any) => {
|
||||||
|
console.log('changeTopText', e.target.value)
|
||||||
|
if (!validateInput(e.target.value)) {
|
||||||
|
showToast('输入的文字不能包含特殊符号!')
|
||||||
|
shapeText.value = e.target.value.replace(/[^a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]/g, '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleInput(e.target.value, 'top')
|
||||||
|
const textPos = shapeList.value.find((item: any) => item.id === shapeId.value)?.text_pos?.find((d: any) => d.position_place == 'top')
|
||||||
|
if (textPos) {
|
||||||
|
textPos.frame_path = shapeImage.value;
|
||||||
|
textShow(textPos, 'top')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bottomText = ref('')
|
||||||
|
const changeBottomText = (e: any) => {
|
||||||
|
console.log('changeBottomText', e.target.value)
|
||||||
|
if (!validateInput(e.target.value)) {
|
||||||
|
showToast('输入的文字不能包含特殊符号!')
|
||||||
|
shapeText.value = e.target.value.replace(/[^a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]/g, '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleInput(e.target.value, 'bottom')
|
||||||
|
const textPos = shapeList.value.find((item: any) => item.id === shapeId.value)?.text_pos?.find((d: any) => d.position_place == 'bottom')
|
||||||
|
if (textPos) {
|
||||||
|
textPos.frame_path = shapeImage.value;
|
||||||
|
textShow(textPos, 'bottom')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const frontSaveText = ref('')
|
||||||
|
const changeFrontText = (e: any) => {
|
||||||
|
// 由于使用了 v-model,frontText.value 已经自动更新
|
||||||
|
const inputValue = e?.target?.value ?? frontText.value
|
||||||
|
console.log('changeFrontText', inputValue)
|
||||||
|
frontText.value = inputValue
|
||||||
|
frontSaveText.value = inputValue
|
||||||
|
handleInput(inputValue)
|
||||||
|
// 实时显示文字
|
||||||
|
if (typeId.value == 6 && shapeList.value.length > 0) {
|
||||||
|
const currentShape = shapeList.value.find((item: any) => item.id === shapeId.value)
|
||||||
|
if (currentShape && currentShape.axisz == 1) {
|
||||||
|
frontTextShow(currentShape, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const backSaveText = ref('')
|
||||||
|
const changeBackText = (e: any) => {
|
||||||
|
// 由于使用了 v-model,backText.value 已经自动更新
|
||||||
|
const inputValue = e?.target?.value ?? backText.value
|
||||||
|
console.log('changeBackText', inputValue)
|
||||||
|
backText.value = inputValue
|
||||||
|
backSaveText.value = inputValue
|
||||||
|
handleInput(inputValue)
|
||||||
|
// 实时显示文字
|
||||||
|
if (typeId.value == 6 && shapeList.value.length > 0) {
|
||||||
|
const currentShape = shapeList.value.find((item: any) => item.id === shapeId.value)
|
||||||
|
if (currentShape && currentShape.axisz == -1) {
|
||||||
|
frontTextShow(currentShape, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const validateInput = (input: string) => {
|
const validateInput = (input: string) => {
|
||||||
// 正则说明:只允许字母(大小写)、数字、中文和空格
|
const regex = /^[a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]+$/;
|
||||||
const regex = /^[a-zA-Z0-9\u4e00-\u9fa5\s]+$/;
|
|
||||||
return regex.test(input);
|
return regex.test(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleInput = (value: string) => {
|
const handleInput = (value: string, type: string = 'shape') => {
|
||||||
let byteLength = calculateByteLength(value);
|
let byteLength = calculateByteLength(value);
|
||||||
console.log('byteLength', byteLength, limitCount.value)
|
console.log('byteLength', byteLength, limitCount.value)
|
||||||
if (byteLength > limitCount.value) {
|
if (byteLength > limitCount.value) {
|
||||||
@@ -798,10 +1075,60 @@ const handleInput = (value: string) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (typeId.value == 2) {
|
||||||
|
if (shapeId.value == 27 || shapeId.value == 28) {
|
||||||
|
type == 'top' ? topText.value = validValue : bottomText.value = validValue;
|
||||||
|
} else {
|
||||||
shapeText.value = validValue;
|
shapeText.value = validValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (axisz.value == 1) {
|
||||||
|
frontText.value = validValue;
|
||||||
|
frontSaveText.value = validValue;
|
||||||
|
} else {
|
||||||
|
backText.value = validValue;
|
||||||
|
backSaveText.value = validValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 实时显示文字(处理长度限制后的值)
|
||||||
|
if (typeId.value == 6 && shapeList.value.length > 0) {
|
||||||
|
const currentShape = shapeList.value.find((item: any) => item.id === shapeId.value)
|
||||||
|
if (currentShape) {
|
||||||
|
if (currentShape.axisz == 1) {
|
||||||
|
frontTextShow(currentShape, true)
|
||||||
|
} else if (currentShape.axisz == -1) {
|
||||||
|
frontTextShow(currentShape, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (typeId.value == 2) {
|
||||||
|
if (shapeId.value == 27 || shapeId.value == 28) {
|
||||||
|
type == 'top' ? topText.value = value : bottomText.value = value;
|
||||||
|
} else {
|
||||||
shapeText.value = value;
|
shapeText.value = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (axisz.value == 1) {
|
||||||
|
frontText.value = value;
|
||||||
|
frontSaveText.value = value;
|
||||||
|
} else {
|
||||||
|
backText.value = value;
|
||||||
|
backSaveText.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 实时显示文字
|
||||||
|
if (typeId.value == 6 && shapeList.value.length > 0) {
|
||||||
|
const currentShape = shapeList.value.find((item: any) => item.id === shapeId.value)
|
||||||
|
if (currentShape) {
|
||||||
|
if (currentShape.axisz == 1) {
|
||||||
|
frontTextShow(currentShape, true)
|
||||||
|
} else if (currentShape.axisz == -1) {
|
||||||
|
frontTextShow(currentShape, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculateByteLength = (str: string) => {
|
const calculateByteLength = (str: string) => {
|
||||||
@@ -835,9 +1162,34 @@ const shapeChange = (item: any) => {
|
|||||||
imageUrl.value = originUrl.value
|
imageUrl.value = originUrl.value
|
||||||
}
|
}
|
||||||
ImageShow(item)
|
ImageShow(item)
|
||||||
if (custom_switch.value == 1) {
|
if (custom_switch.value == 1 && typeId.value == 2) {
|
||||||
|
shapeText.value = ''
|
||||||
|
topText.value = ''
|
||||||
|
bottomText.value = ''
|
||||||
|
topTextStyle.value = ''
|
||||||
|
bottomTextStyle.value = ''
|
||||||
|
shapeTextStyle.value = ''
|
||||||
textShow(item)
|
textShow(item)
|
||||||
}
|
}
|
||||||
|
if (custom_switch.value == 1 && typeId.value == 6) {
|
||||||
|
if (item.axisz == 1) {
|
||||||
|
// 切换到正面
|
||||||
|
axisz.value = 1
|
||||||
|
frontKey.value = 1
|
||||||
|
frontText.value = frontSaveText.value ? frontSaveText.value : ''
|
||||||
|
backText.value = backSaveText.value ? backSaveText.value : ''
|
||||||
|
// 隐藏背面文字,显示正面文字
|
||||||
|
frontTextShow(item, true)
|
||||||
|
} else if (item.axisz == -1) {
|
||||||
|
// 切换到背面
|
||||||
|
axisz.value = -1
|
||||||
|
frontKey.value = 2
|
||||||
|
backText.value = backSaveText.value ? backSaveText.value : ''
|
||||||
|
frontText.value = frontSaveText.value ? frontSaveText.value : ''
|
||||||
|
// 隐藏正面文字,显示背面文字
|
||||||
|
frontTextShow(item, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trialCode = ref(false)
|
const trialCode = ref(false)
|
||||||
@@ -857,18 +1209,18 @@ const getOrderStat = () => {
|
|||||||
console.log('getOrderStat', res)
|
console.log('getOrderStat', res)
|
||||||
orderStat.value = res
|
orderStat.value = res
|
||||||
typeId.value = res.type_id
|
typeId.value = res.type_id
|
||||||
prodId.value = res.prod_id
|
// prodId.value = res.prod_id
|
||||||
|
badgeTypeId.value = res.type_id // 设置 badgeTypeId
|
||||||
getSizeList()
|
getSizeList()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
||||||
localStorage.remove('code')
|
// code 的清除已在 main.ts 中统一处理,这里只处理页面特定的清理逻辑
|
||||||
localStorage.remove('trialCode')
|
|
||||||
localStorage.remove('userId')
|
|
||||||
// 清除轮询和进度条计时器
|
// 清除轮询和进度条计时器
|
||||||
clearInterval(timer.value)
|
clearInterval(timer.value)
|
||||||
clearInterval(progressTimer.value)
|
clearInterval(progressTimer.value)
|
||||||
|
// trialCode 和 userId 的清除由 main.ts 统一处理
|
||||||
}
|
}
|
||||||
|
|
||||||
const pid = ref(0)
|
const pid = ref(0)
|
||||||
@@ -1446,6 +1798,14 @@ onUnmounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
.shape-box-input-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
.shape-box-input-text {
|
.shape-box-input-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -1461,6 +1821,9 @@ onUnmounted(() => {
|
|||||||
text-shadow: 1px 0px 1px #000000a1;
|
text-shadow: 1px 0px 1px #000000a1;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
.font-chinese {
|
||||||
|
font-family: chinese;
|
||||||
|
}
|
||||||
.shape-item-image-round {
|
.shape-item-image-round {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-order">
|
||||||
|
<div class="header" @click="goBack">
|
||||||
|
<span class="back-icon"><van-icon name="arrow-left" size="16px" color="#333" /> {{ toValueWithout(config.myOrder.messages.title) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-list" v-if="orders.length > 0">
|
||||||
|
<div class="order-item" @click="OrderDetail(item.id)" v-for="item in orders" :key="item.id">
|
||||||
|
<img class="order-img" :src="item.path" alt="order1" />
|
||||||
|
<span class="order-status status-finished">{{ getStatus(item.status) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="record-image-item-null">
|
||||||
|
<img :src="nullDataImage" :alt="toValueWithout(config.myOrder.messages.noData)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import * as badgeApi from '@/api/badge'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { toValueWithout } from '@/lang/utils'
|
||||||
|
import { cartoonConfig as config } from '@/config/cartoon'
|
||||||
|
import nullDataImage from '@/assets/badge/null-data.png'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const page = ref(config.myOrder.pagination.initialPage)
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
const orders = ref([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getOrder()
|
||||||
|
})
|
||||||
|
|
||||||
|
function getOrder() {
|
||||||
|
if(loading.value) return
|
||||||
|
loading.value = true
|
||||||
|
badgeApi.getMyOrder({
|
||||||
|
page: page.value,
|
||||||
|
size: config.myOrder.pagination.pageSize,
|
||||||
|
status: config.myOrder.pagination.defaultStatus,
|
||||||
|
}).then((res: any) => {
|
||||||
|
if(page.value === config.myOrder.pagination.initialPage) {
|
||||||
|
orders.value = res.list
|
||||||
|
} else {
|
||||||
|
orders.value.push(...res.list)
|
||||||
|
}
|
||||||
|
if(res.total <= orders.value.length) {
|
||||||
|
finished.value = true
|
||||||
|
} else {
|
||||||
|
page.value++
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.push(config.routes.home)
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrderDetail(id: number) {
|
||||||
|
router.push({
|
||||||
|
path: config.routes.orderDetail,
|
||||||
|
query: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus(status: number) {
|
||||||
|
const statusText = config.myOrder.statusText[status]
|
||||||
|
if (statusText) {
|
||||||
|
return toValueWithout(statusText)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #222;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: v-bind('config.myOrder.styles.gap + "px"');
|
||||||
|
padding: v-bind('config.myOrder.styles.padding.vertical + "px"') v-bind('config.myOrder.styles.padding.horizontal + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item {
|
||||||
|
position: relative;
|
||||||
|
width: v-bind('config.myOrder.styles.itemWidth');
|
||||||
|
height: v-bind('config.myOrder.styles.itemHeight');
|
||||||
|
box-shadow: v-bind('config.myOrder.styles.shadow');
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: v-bind('config.myOrder.styles.borderRadius + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status {
|
||||||
|
position: absolute;
|
||||||
|
bottom: v-bind('config.myOrder.styles.status.bottom + "px"');
|
||||||
|
right: v-bind('config.myOrder.styles.status.right + "px"');
|
||||||
|
padding: v-bind('config.myOrder.styles.status.padding.vertical + "px"') v-bind('config.myOrder.styles.status.padding.horizontal + "px"');
|
||||||
|
border-radius: v-bind('config.myOrder.styles.status.borderRadius + "px"');
|
||||||
|
font-size: v-bind('config.myOrder.styles.status.fontSize + "px"');
|
||||||
|
color: v-bind('config.myOrder.statusStyles.default.color');
|
||||||
|
background: v-bind('config.myOrder.statusStyles.default.background');
|
||||||
|
opacity: v-bind('config.myOrder.statusStyles.default.opacity');
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-processing {
|
||||||
|
background: v-bind('config.myOrder.statusStyles.processing.background');
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-finished {
|
||||||
|
background: v-bind('config.myOrder.statusStyles.finished.background');
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-null {
|
||||||
|
height: 50vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-null img {
|
||||||
|
width: 80vw;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-order">
|
||||||
|
<div class="header" @click="goBack">
|
||||||
|
<span class="back-icon"><van-icon name="arrow-left" size="16px" color="#333" /> {{ toValueWithout(config.orderDetail.messages.title) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-status">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<div class="avatar-front-box" v-if="order.type_id != config.orderDetail.productTypes.reliefPendant">
|
||||||
|
<img class="avatar-font-img" :src="imageUrl" :alt="order.kind_name" />
|
||||||
|
</div>
|
||||||
|
<div class="avatar-front-box-double" v-if="order.type_id == config.orderDetail.productTypes.reliefPendant">
|
||||||
|
<img class="avatar-font-img" :src="imageUrl" />
|
||||||
|
<img class="avatar-font-img" :src="backImageUrl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="status-title">{{ getStatus(order.status) }}</div>
|
||||||
|
<div class="status-desc">
|
||||||
|
{{ toValueWithout(config.orderDetail.messages.statusDesc) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :style="`padding: 0 ${config.orderDetail.styles.padding.horizontal}px;`">
|
||||||
|
<van-divider />
|
||||||
|
</div>
|
||||||
|
<div class="product-info" v-if="order.use_type == config.orderDetail.orderTypes.shipping">
|
||||||
|
<div class="product-title">{{ toValueWithout(config.orderDetail.messages.shippingInfo) }}</div>
|
||||||
|
<div class="product-details">
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.contactInfo) }}</span>
|
||||||
|
<span class="product-value">{{order.contact_name}} {{order.contact_mobile}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.shippingAddress) }}</span>
|
||||||
|
<span class="product-value">{{order.province_name}} {{order.city_name}} {{order.county_name}} {{order.address}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :style="`padding: 0 ${config.orderDetail.styles.padding.horizontal}px;`" v-if="order.use_type == config.orderDetail.orderTypes.shipping">
|
||||||
|
<van-divider />
|
||||||
|
</div>
|
||||||
|
<div class="product-info">
|
||||||
|
<div class="product-title">{{ toValueWithout(config.orderDetail.messages.productInfo) }}</div>
|
||||||
|
<div class="product-details">
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.productName) }}</span>
|
||||||
|
<span class="product-value">
|
||||||
|
{{ toValueWithout(order.prod_name) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.size) }}</span>
|
||||||
|
<span class="product-value">{{order.model_size}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="product-row" v-if="shape_name && order.type_id != config.orderDetail.productTypes.reliefPendant">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.shape) }}</span>
|
||||||
|
<span class="product-value">{{ toValueWithout(shape_name) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.quantity) }}</span>
|
||||||
|
<span class="product-value">{{order.count}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :style="`padding: 0 ${config.orderDetail.styles.padding.horizontal}px;`">
|
||||||
|
<van-divider />
|
||||||
|
</div>
|
||||||
|
<div class="product-info">
|
||||||
|
<div class="product-title">{{ toValueWithout(config.orderDetail.messages.orderInfo) }}</div>
|
||||||
|
<div class="product-details">
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.orderId) }}</span>
|
||||||
|
<span class="product-value">{{order.id}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">{{ toValueWithout(config.orderDetail.messages.orderTime) }}</span>
|
||||||
|
<span class="product-value">{{order.created_at}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :style="`height: ${config.orderDetail.styles.bottomSpacing}px;`"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import * as badgeApi from '@/api/badge';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { toValueWithout } from '@/lang/utils';
|
||||||
|
import { cartoonConfig as config } from '@/config/cartoon';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const order = ref({})
|
||||||
|
const id = ref(0)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
id.value = route.query.id as any
|
||||||
|
getOrderDetail()
|
||||||
|
})
|
||||||
|
|
||||||
|
const shapeImage = ref('')
|
||||||
|
const imageUrl = ref('')
|
||||||
|
const backImageUrl = ref('')
|
||||||
|
const getStyle = ref('')
|
||||||
|
const shape_name = ref('')
|
||||||
|
const imageList = ref([])
|
||||||
|
|
||||||
|
function getOrderDetail() {
|
||||||
|
badgeApi.getOrderDetail({
|
||||||
|
id: id.value,
|
||||||
|
}).then((res: any) => {
|
||||||
|
order.value = res;
|
||||||
|
imageUrl.value = res?.path;
|
||||||
|
backImageUrl.value = res?.back_path;
|
||||||
|
shapeImage.value = res?.shape_details?.frame_path || '';
|
||||||
|
shape_name.value = res?.shape_details?.name || '';
|
||||||
|
imageList.value = [
|
||||||
|
res?.path,
|
||||||
|
res?.back_path,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus(status: number) {
|
||||||
|
const statusText = config.myOrder.statusText[status]
|
||||||
|
if (statusText) {
|
||||||
|
return toValueWithout(statusText)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #222;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vw;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: v-bind('config.orderDetail.imageSize.width + "px"');
|
||||||
|
height: v-bind('config.orderDetail.imageSize.height + "px"');
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-back-img {
|
||||||
|
position: absolute;
|
||||||
|
width: v-bind('config.orderDetail.imageSize.width + "px"');
|
||||||
|
height: v-bind('config.orderDetail.imageSize.height + "px"');
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-front-box {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-font-img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: v-bind('config.orderDetail.imageSize.height + "px"');
|
||||||
|
border-radius: v-bind('config.orderDetail.imageSize.borderRadius + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-front-box-double {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: v-bind('config.orderDetail.styles.avatar.gap + "px"');
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: v-bind('config.orderDetail.styles.statusInfo.padding.vertical + "px"') v-bind('config.orderDetail.styles.statusInfo.padding.horizontal + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-title {
|
||||||
|
font-size: v-bind('config.orderDetail.styles.statusInfo.title.fontSize + "px"');
|
||||||
|
font-weight: v-bind('config.orderDetail.styles.statusInfo.title.fontWeight');
|
||||||
|
color: v-bind('config.orderDetail.styles.statusInfo.title.color');
|
||||||
|
margin-bottom: v-bind('config.orderDetail.styles.statusInfo.title.marginBottom + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-desc {
|
||||||
|
font-size: v-bind('config.orderDetail.styles.statusInfo.desc.fontSize + "px"');
|
||||||
|
color: v-bind('config.orderDetail.styles.statusInfo.desc.color');
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info {
|
||||||
|
width: 100%;
|
||||||
|
padding: v-bind('config.orderDetail.styles.productInfo.padding.vertical + "px"') v-bind('config.orderDetail.styles.productInfo.padding.horizontal + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-title {
|
||||||
|
font-size: v-bind('config.orderDetail.styles.productInfo.title.fontSize + "px"');
|
||||||
|
font-weight: v-bind('config.orderDetail.styles.productInfo.title.fontWeight');
|
||||||
|
color: v-bind('config.orderDetail.styles.productInfo.title.color');
|
||||||
|
margin-bottom: v-bind('config.orderDetail.styles.productInfo.title.marginBottom + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: v-bind('config.orderDetail.styles.productInfo.details.gap + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-label {
|
||||||
|
color: v-bind('config.orderDetail.styles.productInfo.label.color');
|
||||||
|
font-size: v-bind('config.orderDetail.styles.productInfo.label.fontSize + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-value {
|
||||||
|
color: v-bind('config.orderDetail.styles.productInfo.value.color');
|
||||||
|
font-size: v-bind('config.orderDetail.styles.productInfo.value.fontSize + "px"');
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="header" @click="goBack" style="text-align: center;">
|
||||||
|
<span class="back-icon"><van-icon name="arrow-left" size="16px" color="#333" /> {{ toValueWithout(config.photoAlbum.messages.title) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="record-container">
|
||||||
|
<div class="record-note">{{ toValueWithout(config.photoAlbum.messages.note) }}</div>
|
||||||
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||||
|
<van-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
:finished="finished"
|
||||||
|
:finished-text="toValueWithout(config.photoAlbum.messages.noMore)"
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
|
<div class="record-images" v-if="images.length > 0">
|
||||||
|
<div class="record-image-item" v-for="item in images" :key="item.id">
|
||||||
|
<block v-if="item.status == config.photoAlbum.status.success" @click="goPreview(item)">
|
||||||
|
<img :src="item.path" :alt="item.name" class="record-image">
|
||||||
|
<div class="record-image-item-shadow"></div>
|
||||||
|
</block>
|
||||||
|
<div v-else-if="item.status == config.photoAlbum.status.designing || item.reason" class="record-image-item-loading">
|
||||||
|
<div style="font-size: 12px;color: #999;text-align: center;">ID: {{ item.sw_pid }}</div>
|
||||||
|
<div style="width: 70%;font-size: 12px;">{{ item.reason || toValueWithout(config.photoAlbum.messages.designing) }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="item.status == config.photoAlbum.status.failed || item.reason" class="record-image-item-loading">
|
||||||
|
<div style="font-size: 12px;color: #999;text-align: center;">ID: {{ item.sw_pid }}</div>
|
||||||
|
<div style="width: 70%;font-size: 12px;">{{ item.reason || toValueWithout(config.photoAlbum.messages.designFailed) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="loading && page.value === config.photoAlbum.pagination.initialPage" class="record-image-item-loading">
|
||||||
|
{{ toValueWithout(config.photoAlbum.messages.loading) }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="record-image-item-null">
|
||||||
|
<img :src="nullDataImage" :alt="toValueWithout(config.photoAlbum.messages.noData)">
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</van-pull-refresh>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import * as badgeApi from '@/api/badge';
|
||||||
|
import { onMounted, ref, onUnmounted } from 'vue';
|
||||||
|
import { toValueWithout } from '@/lang/utils';
|
||||||
|
import { cartoonConfig as config } from '@/config/cartoon';
|
||||||
|
import nullDataImage from '@/assets/badge/null-data.png';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const images = ref([]);
|
||||||
|
const page = ref(config.photoAlbum.pagination.initialPage);
|
||||||
|
const loading = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
const error = ref(null);
|
||||||
|
const refreshing = ref(false);
|
||||||
|
|
||||||
|
// 获取设计记录列表
|
||||||
|
async function getList() {
|
||||||
|
// 如果正在加载或者已经加载完所有数据,则返回
|
||||||
|
console.log(loading.value, finished.value);
|
||||||
|
if (loading.value || finished.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await badgeApi.getLogOage({
|
||||||
|
page: page.value,
|
||||||
|
size: config.photoAlbum.pagination.pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (page.value === config.photoAlbum.pagination.initialPage) {
|
||||||
|
images.value = res.list || [];
|
||||||
|
} else {
|
||||||
|
images.value.push(...(res.list || []));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否还有更多数据
|
||||||
|
if (!res.list || res.list.length < config.photoAlbum.pagination.pageSize) {
|
||||||
|
finished.value = true;
|
||||||
|
} else {
|
||||||
|
page.value++;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
error.value = err.message || toValueWithout(config.photoAlbum.messages.getDataFailed);
|
||||||
|
console.error('获取设计记录失败', err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看设计预览
|
||||||
|
function goPreview(item) {
|
||||||
|
router.push({
|
||||||
|
path: config.routes.preview,
|
||||||
|
query: {
|
||||||
|
pid: item.sw_pid,
|
||||||
|
group: item.group_count,
|
||||||
|
key: item.num,
|
||||||
|
prod_id: item.prod_id
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
function onLoad() {
|
||||||
|
loading.value = false;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
async function onRefresh() {
|
||||||
|
refreshing.value = true;
|
||||||
|
page.value = config.photoAlbum.pagination.initialPage;
|
||||||
|
finished.value = false;
|
||||||
|
await getList();
|
||||||
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
function goBack() {
|
||||||
|
router.push('/cartoon');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时清理
|
||||||
|
onUnmounted(() => {
|
||||||
|
loading.value = false;
|
||||||
|
finished.value = false;
|
||||||
|
page.value = config.photoAlbum.pagination.initialPage;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #222;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-note {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-images {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(v-bind('config.photoAlbum.styles.gridColumns'), 1fr);
|
||||||
|
gap: v-bind('config.photoAlbum.styles.gap + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: v-bind('config.photoAlbum.styles.borderRadius + "px"');
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-shadow {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
box-shadow: inset 2px 2px 4px rgba(255, 255, 255, 0.5), inset -2px -2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #f5f5f5;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-null {
|
||||||
|
height: 50vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-image-item-null img {
|
||||||
|
width: 80vw;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -144,6 +144,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
port: 80,
|
port: 80,
|
||||||
|
// https: false,
|
||||||
https: true,
|
https: true,
|
||||||
proxy,
|
proxy,
|
||||||
// proxy: env.VITE_HTTP_MOCK === 'true'
|
// proxy: env.VITE_HTTP_MOCK === 'true'
|
||||||
|
|||||||