Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7aeb2f6ce | ||
|
|
925d6b105f | ||
|
|
9ede62f450 | ||
|
|
43471c3a76 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
NODE_ENV=production
|
||||
VITE_APP_PREVIEW=false
|
||||
VITE_APP_API_BASE_URL=https://wechat.api.puabadge.com/web/
|
||||
VITE_APP_API_URL=https://wechat.api.puabadge.com/web/
|
||||
VITE_APP_API_WX_URL=https://wechat.api.puabadge.com/web/
|
||||
WECHAT_AUTH_URL=https://wechat.api.puabadge.com/api/auth/page
|
||||
VITE_APP_API_BASE_URL=https://shop.api.suwa3d.com/api/web/
|
||||
VITE_APP_API_URL=https://shop.api.suwa3d.com/api/web/
|
||||
VITE_APP_API_WX_URL=https://shop.api.suwa3d.com/api/web/
|
||||
WECHAT_AUTH_URL=https://shop.api.suwa3d.com/api/auth/page
|
||||
@@ -13,11 +13,16 @@ declare module '@vue/runtime-core' {
|
||||
Backup: typeof import('./src/components/arFrame/backup.vue')['default']
|
||||
Backup2: typeof import('./src/components/arFrame/backup2.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']
|
||||
Hint: typeof import('./src/components/hint/hint.vue')['default']
|
||||
Loading: typeof import('./src/components/loading/index.vue')['default']
|
||||
@@ -27,11 +32,11 @@ declare module '@vue/runtime-core' {
|
||||
Tensorflow: typeof import('./src/components/arFrame/tensorflow.vue')['default']
|
||||
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
||||
VanAddressEdit: typeof import('vant/es')['AddressEdit']
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanList: typeof import('vant/es')['List']
|
||||
VanLoading: typeof import('vant/es')['Loading']
|
||||
VanOverlay: typeof import('vant/es')['Overlay']
|
||||
VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
||||
VanStepper: typeof import('vant/es')['Stepper']
|
||||
VanUploader: typeof import('vant/es')['Uploader']
|
||||
|
||||
@@ -162,7 +162,39 @@ export const getMultiUrl = (params: any) => {
|
||||
|
||||
// 获取坐骑列表
|
||||
export const getMountsList = (params: any) => {
|
||||
return request('products/mountsList', {
|
||||
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 |
@@ -110,7 +110,7 @@ export const cartoonConfig = {
|
||||
},
|
||||
spacing: {
|
||||
pagePadding: '16px',
|
||||
sectionMargin: '16px',
|
||||
sectionMargin: '8px',
|
||||
itemGap: '8px'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -158,5 +158,50 @@ const en = {
|
||||
"请输入背面文字": "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
|
||||
@@ -158,5 +158,50 @@ const ko = {
|
||||
"请输入背面文字": "뒷면 텍스트를 입력하세요",
|
||||
"请输入顶部文字": "상단 텍스트를 입력하세요",
|
||||
"请输入底部文字": "하단 텍스트를 입력하세요",
|
||||
"坐骑类型": "좌석 유형",
|
||||
"智能生成": "스마트 생성",
|
||||
"自定义": "사용자 정의",
|
||||
"请选择坐骑类型": "좌석 유형을 선택하세요",
|
||||
"场景道具": "장소 도구",
|
||||
"请输入场景道具": "장소 도구를 입력하세요",
|
||||
"手办摇摇乐": "피규어 흔들림",
|
||||
"手办挂饰": "피규어 걸이",
|
||||
"场景手办": "장소 피규어",
|
||||
"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
|
||||
@@ -158,5 +158,53 @@ const zhCN = {
|
||||
"请输入背面文字": "请输入背面文字",
|
||||
"请输入顶部文字": "请输入顶部文字",
|
||||
"请输入底部文字": "请输入底部文字",
|
||||
"坐骑类型": "坐骑类型",
|
||||
"智能生成": "智能生成",
|
||||
"自定义": "自定义",
|
||||
"请选择坐骑类型": "请选择坐骑类型",
|
||||
"场景道具": "场景道具",
|
||||
"请输入场景道具": "请输入场景道具",
|
||||
"手办摇摇乐": "手办摇摇乐",
|
||||
"手办挂饰": "手办挂饰",
|
||||
"场景手办": "场景手办",
|
||||
"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
|
||||
@@ -158,5 +158,50 @@ const zhCT = {
|
||||
"请输入背面文字": "請輸入背面文字",
|
||||
"请输入顶部文字": "請輸入頂部文字",
|
||||
"请输入底部文字": "請輸入底部文字",
|
||||
"坐骑类型": "坐騎類型",
|
||||
"智能生成": "智能生成",
|
||||
"自定义": "自定義",
|
||||
"请选择坐骑类型": "請選擇坐騎類型",
|
||||
"场景道具": "場景道具",
|
||||
"请输入场景道具": "請輸入場景道具",
|
||||
"手办摇摇乐": "手辦搖搖樂",
|
||||
"手办挂饰": "手辦掛飾",
|
||||
"场景手办": "場景手辦",
|
||||
"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
|
||||
@@ -88,53 +88,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-line" v-if="shouldShowPhotoExample">
|
||||
<div class="step-line-item">
|
||||
<div class="step-line-item-title">
|
||||
<span style="display: flex; align-items: center;justify-content: center;">
|
||||
<span :style="`height: 1px; background: ${config.styles.colors.border};width: 50px;`"></span>
|
||||
<span :style="`margin: 0 8px; color: ${config.styles.colors.textTertiary};`">{{ toValueWithout("照片示例") }}</span>
|
||||
<span :style="`height: 1px; background: ${config.styles.colors.border};width: 50px;`"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="step-line-item-desc">
|
||||
<div class="step-line-item-desc-item">
|
||||
<img :src="example1" :style="`width: 20vw;height: 20vw;border-radius: 8px;`" alt="">
|
||||
</div>
|
||||
<div class="step-line-item-desc-item">
|
||||
<img :src="example2" :style="`width: 20vw;height: 20vw;border-radius: 8px;`" alt="">
|
||||
</div>
|
||||
<div class="step-line-item-desc-item">
|
||||
<img :src="example3" :style="`width: 20vw;height: 20vw;border-radius: 8px;`" alt="">
|
||||
</div>
|
||||
<div class="step-line-item-desc-item">
|
||||
<img :src="example4" :style="`width: 20vw;height: 20vw;border-radius: 8px;`" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="photo-upload-body">
|
||||
<div v-if="!picture" class="photo-upload-box">
|
||||
<div class="photo-upload-area">
|
||||
<div class="photo-upload-plus">
|
||||
+
|
||||
</div>
|
||||
<div class="photo-upload-text">
|
||||
{{ toValueWithout("点击上传照片") }}
|
||||
</div>
|
||||
<div class="photo-upload-tip">
|
||||
*{{ toValueWithout("上传照片时建议勾选[原图]") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="photo-upload-footer">
|
||||
{{ toValueWithout("请确定您对上传的照片拥有合法使用权利或已取得他人合法授权,且同意本平台分析图片信息以提供生成服务") }}
|
||||
</div>
|
||||
</div>
|
||||
<img class="photo-upload-img" v-if="picture" :src="picture" alt="" srcset="">
|
||||
<div class="photo-upload-box-1">
|
||||
<h5-cropper :option="option" @getbase64Data="getbase64Data" @imgorigoinf="imgorigoinf"></h5-cropper>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="shouldShowKindList">
|
||||
<div :style="`font-size: 16px; color: ${config.styles.colors.textPrimary}; font-weight: bold; margin-top: ${config.styles.spacing.sectionMargin};padding: 0 ${config.styles.spacing.pagePadding};`">
|
||||
{{ toValueWithout("风格类型") }}
|
||||
@@ -147,7 +100,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="shouldShowMultiView">
|
||||
<div v-if="isViews == 1">
|
||||
<div :style="`font-size: 16px; color: ${config.styles.colors.textPrimary}; font-weight: bold; margin-top: ${config.styles.spacing.sectionMargin};padding: 0 ${config.styles.spacing.pagePadding};`">
|
||||
{{ toValueWithout("多视角参考图") }}
|
||||
</div>
|
||||
@@ -176,7 +129,7 @@
|
||||
<div class="mounts-item-list">
|
||||
<img
|
||||
class="mounts-item-image"
|
||||
:src="getImageUrl(item.cover_path)"
|
||||
:src="item.image"
|
||||
:alt="toValueWithout(item.name)"
|
||||
/>
|
||||
<van-icon
|
||||
@@ -241,6 +194,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="photo-upload-body">
|
||||
<div v-if="!picture" class="photo-upload-box">
|
||||
<div class="photo-upload-area">
|
||||
<div class="photo-upload-plus">
|
||||
+
|
||||
</div>
|
||||
<div class="photo-upload-text">
|
||||
{{ toValueWithout("点击上传照片") }}
|
||||
</div>
|
||||
<div class="photo-upload-tip">
|
||||
*{{ toValueWithout("上传照片时建议勾选[原图]") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="photo-upload-footer">
|
||||
{{ toValueWithout("请确定您对上传的照片拥有合法使用权利或已取得他人合法授权,且同意本平台分析图片信息以提供生成服务") }}
|
||||
</div>
|
||||
</div>
|
||||
<img class="photo-upload-img" v-if="picture" :src="picture" alt="" srcset="">
|
||||
<div class="photo-upload-box-1">
|
||||
<h5-cropper :option="option" @getbase64Data="getbase64Data" @imgorigoinf="imgorigoinf"></h5-cropper>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="`height: 120px;`"></div>
|
||||
<div class="design-action-bar">
|
||||
<div class="design-left">
|
||||
@@ -268,11 +243,6 @@ import { toValueWithout } from '@/lang/utils'
|
||||
import { cartoonConfig } from '@/config/cartoon'
|
||||
import leafIcon from '@/assets/badge/leaf.png'
|
||||
import arrowIcon from '@/assets/badge/arrow.png'
|
||||
import example1 from '@/assets/badge/1.png'
|
||||
import example2 from '@/assets/badge/2.png'
|
||||
import example3 from '@/assets/badge/3.png'
|
||||
import example4 from '@/assets/badge/4.png'
|
||||
// Element Plus 图标通过组件自动导入,这里使用文本符号作为替代
|
||||
|
||||
// 初始化翻译功能
|
||||
const { t } = useTranslation();
|
||||
@@ -332,6 +302,7 @@ const typeId = ref(0)
|
||||
const subjectId = ref(0)
|
||||
const typeName = ref('')
|
||||
const subjectName = ref('')
|
||||
const isViews = ref(0)
|
||||
const getOrderStat = () => {
|
||||
badgeApi.getOrderStat({}).then((res: any) => {
|
||||
orderStat.value = res
|
||||
@@ -341,9 +312,9 @@ const getOrderStat = () => {
|
||||
typeName.value = res.type_name
|
||||
subjectId.value = res.subject_id
|
||||
subjectName.value = res.subject_name
|
||||
if (res.type_id === 4) {
|
||||
getKindList()
|
||||
} else if (res.type_id === 8) {
|
||||
isViews.value = res.is_views
|
||||
getKindList()
|
||||
if (res.type_id === 8) {
|
||||
mountsId.value = 0
|
||||
getmountsList()
|
||||
} else if (res.type_id === 9) {
|
||||
@@ -361,12 +332,7 @@ const shouldShowPhotoExample = computed(() => {
|
||||
|
||||
// 计算属性:是否显示风格类型
|
||||
const shouldShowKindList = computed(() => {
|
||||
return typeId.value === 4 && kindList.value.length > 0
|
||||
})
|
||||
|
||||
// 计算属性:是否显示多视角参考图
|
||||
const shouldShowMultiView = computed(() => {
|
||||
return typeId.value === 4 && config.productLimits.multiViewSupported.includes(prodId.value)
|
||||
return kindList.value.length > 0
|
||||
})
|
||||
|
||||
const kindList = ref([])
|
||||
@@ -403,7 +369,10 @@ const getmountsList = () => {
|
||||
}
|
||||
badgeApi.getMountsList({
|
||||
prod_id: prodId.value,
|
||||
type_id: typeId.value
|
||||
type_id: typeId.value,
|
||||
status: 1,
|
||||
page: 1,
|
||||
size: 1000
|
||||
}).then((res: any) => {
|
||||
if (res && res.list && res.list.length > 0) {
|
||||
mountsList.value = res.list
|
||||
@@ -645,7 +614,7 @@ const getPid = async () => {
|
||||
sendToOss(imgurl.value, res.url),
|
||||
]
|
||||
await Promise.all(uploadTasks)
|
||||
if (shouldShowMultiView.value && referPicture.value.length > 0) {
|
||||
if (isViews.value == 1 && referPicture.value.length > 0) {
|
||||
const multiUrlTasks = referPicture.value.map((item) => {
|
||||
return badgeApi.getMultiUrl({
|
||||
pid: pid.value,
|
||||
@@ -1064,7 +1033,7 @@ onMounted(() => {
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px 36px;
|
||||
padding: 16px;
|
||||
}
|
||||
.kind-box-item {
|
||||
margin-right: 10px;
|
||||
@@ -1114,7 +1083,7 @@ onMounted(() => {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
@@ -103,9 +103,149 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 后视图 -->
|
||||
<div>
|
||||
<div v-if="isViews === 1" style="padding: 0 16px; margin-top: 16px;">
|
||||
<div :style="`font-size: 16px; color: ${config.styles.colors.textPrimary}; font-weight: bold; margin-bottom: 12px;`">
|
||||
{{ toValueWithout("后视图") }}
|
||||
</div>
|
||||
<div :style="`font-size: 14px; color: ${config.styles.colors.textSecondary}; margin-bottom: 12px; line-height: 1.6;`">
|
||||
<p>{{ toValueWithout("多主体手办下单前需先生成正确的后视图") }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 提示词输入框 -->
|
||||
<div style="margin-bottom: 12px; position: relative;">
|
||||
<el-input
|
||||
v-model="rearViewPromptText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:placeholder="toValueWithout('请输入提示词,描述需要生成的后视图要求')"
|
||||
:disabled="isUsingPresetPrompt"
|
||||
class="rear-view-textarea"
|
||||
@input="handlePromptTextInput"
|
||||
/>
|
||||
<el-button
|
||||
v-if="isUsingPresetPrompt"
|
||||
type="text"
|
||||
size="small"
|
||||
style="position: absolute; bottom: 8px; right: 8px;"
|
||||
@click="enableEditPrompt"
|
||||
>
|
||||
{{ toValueWithout("编辑") }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div style="display: flex; gap: 12px; margin-bottom: 16px;">
|
||||
<el-button
|
||||
type="success"
|
||||
@click="handleGenerateRearView"
|
||||
:loading="generatingRearView"
|
||||
:disabled="hasGeneratingRearView || generatingRearView"
|
||||
>
|
||||
{{ toValueWithout("生成后视图") }}
|
||||
</el-button>
|
||||
<el-button type="default" @click="handlePresetRearViewPrompt">
|
||||
{{ toValueWithout("预设提示词") }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 后视图记录 -->
|
||||
<div class="rear-view-record-section">
|
||||
<el-tabs v-model="rearViewActiveTab">
|
||||
<el-tab-pane :label="toValueWithout('后视图记录')" name="record">
|
||||
<div class="rear-view-image-grid">
|
||||
<!-- 生成中的占位 -->
|
||||
<div v-if="generatingRearView && rearViewList.length === 0" class="rear-view-image-item loading">
|
||||
<div class="loading-placeholder">
|
||||
{{ toValueWithout("生成中...") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已生成的后视图 -->
|
||||
<div
|
||||
v-for="(item, index) in rearViewList"
|
||||
:key="item.unique_no || item.id || index"
|
||||
class="rear-view-image-item"
|
||||
:class="{ active: selectedRearViewIndex === index }"
|
||||
@click="selectRearViewImage(index)"
|
||||
>
|
||||
<!-- 图片为空时显示生成中 -->
|
||||
<div v-if="item.status == 0" class="loading-placeholder">
|
||||
<van-loading color="#1989fa" />
|
||||
<span>{{ toValueWithout("生成中,请稍后...") }}</span>
|
||||
</div>
|
||||
<el-image
|
||||
v-else
|
||||
:src="getRearViewImageUrl(item.url)"
|
||||
fit="cover"
|
||||
class="rear-view-image"
|
||||
/>
|
||||
<div v-if="selectedRearViewIndex === index" class="rear-view-image-actions">
|
||||
<el-button
|
||||
size="small"
|
||||
:disabled="item.status == 0"
|
||||
@click.stop="handleMirrorRearView(index)"
|
||||
>
|
||||
{{ toValueWithout("镜像") }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
size="small"
|
||||
:disabled="item.status == 0"
|
||||
@click.stop="confirmSelectRearView(index)"
|
||||
>
|
||||
{{ toValueWithout("选择") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="toValueWithout('后视图要求')" name="requirements">
|
||||
<div class="rear-view-requirements-content">
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>
|
||||
<div class="requirements-text">
|
||||
<p>{{ toValueWithout("1. 后视图应与前视图保持角色和动作的一致性") }}</p>
|
||||
<p>{{ toValueWithout("2. 模型需要水平旋转180度") }}</p>
|
||||
<p>{{ toValueWithout("3. 确保后视图清晰可见,无明显遮挡") }}</p>
|
||||
<p>{{ toValueWithout("4. 后视图应展示角色的背面特征") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 预设提示词选择弹窗 -->
|
||||
<el-dialog
|
||||
:title="toValueWithout('选择预设提示词')"
|
||||
v-model="presetRearViewPromptDialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="preset-prompt-list">
|
||||
<div
|
||||
v-for="(tip, index) in rearViewTipsList"
|
||||
:key="tip.id || index"
|
||||
class="preset-prompt-item"
|
||||
@click="selectPresetRearViewPrompt(tip)"
|
||||
>
|
||||
<div class="prompt-title">{{ tip.title || `${toValueWithout('提示词')} ${String(index + 1)}` }}</div>
|
||||
<div class="prompt-content">{{ tip.content }}</div>
|
||||
</div>
|
||||
<div v-if="rearViewTipsList.length === 0" class="empty-tips">
|
||||
{{ toValueWithout("暂无预设提示词") }}
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="presetRearViewPromptDialogVisible = false">{{ toValueWithout("取消") }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<div style="padding: 0 16px;">
|
||||
<van-divider />
|
||||
</div>
|
||||
@@ -179,9 +319,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, onUnmounted, nextTick } from 'vue';
|
||||
import { onMounted, ref, onUnmounted, nextTick, watch, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { showSuccessToast, showToast, showConfirmDialog } from 'vant';
|
||||
import { showSuccessToast, showToast, showConfirmDialog, Loading } from 'vant';
|
||||
import { localStorage } from '@/utils/local-storage'
|
||||
import * as badgeApi from '@/api/badge'
|
||||
import { areaList } from '@/utils/area'
|
||||
@@ -966,6 +1106,7 @@ const orderStat = ref({})
|
||||
const typeId = ref(0)
|
||||
const subjectId = ref(0)
|
||||
const typeName = ref('')
|
||||
const isViews = ref(0)
|
||||
const getOrderStat = () => {
|
||||
badgeApi.getOrderStat({}).then((res: any) => {
|
||||
console.log('getOrderStat', res)
|
||||
@@ -974,15 +1115,373 @@ const getOrderStat = () => {
|
||||
subjectId.value = res.subject_id
|
||||
typeName.value = res.type_name
|
||||
badgeTypeId.value = res.type_id
|
||||
isViews.value = res.is_views || 0
|
||||
getSizeList()
|
||||
// 如果是多视图,加载后视图列表
|
||||
if (isViews.value === 1) {
|
||||
loadRearViewList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 后视图相关状态
|
||||
const rearViewActiveTab = ref('record')
|
||||
const generatingRearView = ref(false)
|
||||
const rearViewPromptText = ref('')
|
||||
const isUsingPresetPrompt = ref(false) // 标记是否使用预设提示词
|
||||
const rearViewList = ref<any[]>([])
|
||||
const selectedRearViewIndex = ref<number | null>(null)
|
||||
const selectedRearView = ref<any>(null)
|
||||
const rearViewTipsList = ref<any[]>([])
|
||||
const presetRearViewPromptDialogVisible = ref(false)
|
||||
const rearViewPollingTimer = ref<NodeJS.Timeout | null>(null)
|
||||
const isRearViewPolling = ref(false)
|
||||
const isLoadingRearViewList = ref(false)
|
||||
|
||||
// 检查是否有正在生成的后视图
|
||||
const hasGeneratingRearView = computed(() => {
|
||||
return rearViewList.value.some((item: any) => item.status === 0)
|
||||
})
|
||||
|
||||
// 获取后视图图片URL
|
||||
function getRearViewImageUrl(url: string) {
|
||||
if (!url) return ''
|
||||
if (url.indexOf('http') === 0) {
|
||||
return url
|
||||
}
|
||||
return `https://3dview.suwa3d.com/${url}?t=${Date.now()}`
|
||||
}
|
||||
|
||||
// 处理预设提示词
|
||||
function handlePresetRearViewPrompt() {
|
||||
if (rearViewTipsList.value.length === 0) {
|
||||
showToast({
|
||||
message: toValueWithout('暂无预设提示词'),
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果只有一条数据,直接显示在输入框中
|
||||
if (rearViewTipsList.value.length === 1) {
|
||||
rearViewPromptText.value = rearViewTipsList.value[0].content || ''
|
||||
isUsingPresetPrompt.value = true // 标记为使用预设提示词,禁用输入框
|
||||
return
|
||||
}
|
||||
|
||||
// 如果有多条数据,弹出选择弹窗
|
||||
presetRearViewPromptDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 选择预设提示词
|
||||
function selectPresetRearViewPrompt(tip: any) {
|
||||
rearViewPromptText.value = tip.content || ''
|
||||
isUsingPresetPrompt.value = true // 标记为使用预设提示词
|
||||
presetRearViewPromptDialogVisible.value = false
|
||||
}
|
||||
|
||||
// 处理提示词输入(用户手动输入时)
|
||||
function handlePromptTextInput() {
|
||||
// 如果用户手动输入,取消预设提示词标记
|
||||
isUsingPresetPrompt.value = false
|
||||
}
|
||||
|
||||
// 启用编辑提示词(从预设提示词切换到可编辑模式)
|
||||
function enableEditPrompt() {
|
||||
isUsingPresetPrompt.value = false
|
||||
}
|
||||
|
||||
// 生成后视图
|
||||
async function handleGenerateRearView() {
|
||||
// 检查是否有正在生成的后视图
|
||||
if (hasGeneratingRearView.value) {
|
||||
showToast({
|
||||
message: toValueWithout('请等待前一张后视图生成完成'),
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!pid.value) {
|
||||
showToast({
|
||||
message: toValueWithout('订单ID不能为空'),
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!rearViewPromptText.value || rearViewPromptText.value.trim() === '') {
|
||||
showToast({
|
||||
message: toValueWithout('请输入后视图提示词'),
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
generatingRearView.value = true
|
||||
|
||||
// 调用生成后视图接口
|
||||
await badgeApi.generateViews({
|
||||
pid: pid.value,
|
||||
pos: 3,
|
||||
size_type: 2,
|
||||
prompt: rearViewPromptText.value || ''
|
||||
})
|
||||
|
||||
showSuccessToast({
|
||||
message: toValueWithout('后视图生成提交成功'),
|
||||
duration: 2000,
|
||||
})
|
||||
// 先加载一次数据
|
||||
await loadRearViewList()
|
||||
// 启动轮询
|
||||
startRearViewPolling()
|
||||
} catch (err: any) {
|
||||
console.error('生成后视图提交失败:', err)
|
||||
showToast({
|
||||
message: err.message || toValueWithout('生成后视图提交失败,请重试'),
|
||||
duration: 2000,
|
||||
})
|
||||
} finally {
|
||||
generatingRearView.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择图片
|
||||
function selectRearViewImage(index: number) {
|
||||
selectedRearViewIndex.value = index
|
||||
selectedRearView.value = rearViewList.value[index]
|
||||
}
|
||||
|
||||
// 镜像处理
|
||||
async function handleMirrorRearView(index: number) {
|
||||
if (rearViewList.value[index]) {
|
||||
await badgeApi.flipViews({
|
||||
unique_no: rearViewList.value[index].unique_no,
|
||||
}).then((res: any) => {
|
||||
showSuccessToast({
|
||||
message: toValueWithout('镜像处理成功'),
|
||||
duration: 2000,
|
||||
})
|
||||
// 镜像处理后重新加载,但不影响轮询
|
||||
loadRearViewList(false)
|
||||
}).catch((err: any) => {
|
||||
showToast({
|
||||
message: toValueWithout('镜像处理失败'),
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 确认选择
|
||||
function confirmSelectRearView(index: number) {
|
||||
selectRearViewImage(index)
|
||||
handleConfirmRearView()
|
||||
}
|
||||
|
||||
// 确认后视图
|
||||
function handleConfirmRearView() {
|
||||
if (!selectedRearView.value) {
|
||||
showToast({
|
||||
message: toValueWithout('请先选择后视图'),
|
||||
duration: 2000,
|
||||
})
|
||||
return
|
||||
}
|
||||
badgeApi.confirmViews({
|
||||
unique_no: selectedRearView.value.unique_no,
|
||||
}).then((res: any) => {
|
||||
showSuccessToast({
|
||||
message: toValueWithout('后视图确认成功'),
|
||||
duration: 2000,
|
||||
})
|
||||
}).catch((err: any) => {
|
||||
showToast({
|
||||
message: err.message && err.message.indexOf('文件不存在') > -1 ? toValueWithout('图片不存在') : toValueWithout('后视图确认失败'),
|
||||
duration: 2000,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 加载后视图记录
|
||||
// isPollingRequest: 是否为轮询请求(轮询请求不会更新 tips,避免闪烁)
|
||||
async function loadRearViewList(isPollingRequest: boolean = false) {
|
||||
if (!pid.value) return
|
||||
|
||||
// 防止并发请求
|
||||
if (isLoadingRearViewList.value) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isLoadingRearViewList.value = true
|
||||
|
||||
const res: any = await badgeApi.listViews({
|
||||
pid: pid.value,
|
||||
})
|
||||
console.log('后视图列表===》', res)
|
||||
|
||||
// 保存 tips 数据(只在非轮询请求时更新,避免闪烁)
|
||||
if (!isPollingRequest) {
|
||||
if (res && Array.isArray(res.tips)) {
|
||||
rearViewTipsList.value = res.tips
|
||||
} else {
|
||||
rearViewTipsList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
if (res && Array.isArray(res.list)) {
|
||||
const newList = res.list.map((item: any) => ({
|
||||
url: item.path,
|
||||
id: item.id,
|
||||
unique_no: item.unique_no,
|
||||
status: item.status,
|
||||
}))
|
||||
|
||||
// 无感更新:比较新旧数据,只更新变化的部分
|
||||
await updateRearViewList(newList)
|
||||
|
||||
// 检查是否所有项都已完成(status=1),如果是则停止轮询
|
||||
const hasGenerating = newList.some((item: any) => item.status === 0)
|
||||
if (hasGenerating && isPollingRequest) {
|
||||
// 如果还有生成中的项且是轮询请求,继续轮询
|
||||
// 轮询逻辑在 startRearViewPolling 中处理
|
||||
} else if (!hasGenerating) {
|
||||
// 所有项都已完成,停止轮询
|
||||
stopRearViewPolling()
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载后视图列表失败:', err)
|
||||
// 如果轮询请求失败,停止轮询
|
||||
if (isPollingRequest) {
|
||||
stopRearViewPolling()
|
||||
}
|
||||
} finally {
|
||||
isLoadingRearViewList.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 无感更新后视图列表,避免闪烁
|
||||
async function updateRearViewList(newList: any[]) {
|
||||
// 使用 nextTick 确保在 DOM 更新后执行
|
||||
await nextTick()
|
||||
|
||||
// 如果列表为空,直接替换
|
||||
if (rearViewList.value.length === 0) {
|
||||
rearViewList.value = newList
|
||||
return
|
||||
}
|
||||
|
||||
// 创建以 unique_no 为 key 的映射,方便查找
|
||||
const existingMap = new Map<number | string, any>()
|
||||
rearViewList.value.forEach((item: any, index: number) => {
|
||||
if (item.unique_no) {
|
||||
existingMap.set(item.unique_no, { item, index })
|
||||
}
|
||||
})
|
||||
|
||||
// 记录需要移除的索引(从后往前删除,避免索引变化)
|
||||
const toRemove: number[] = []
|
||||
const newUniqueNos = new Set(newList.map((item: any) => item.unique_no).filter(Boolean))
|
||||
|
||||
// 标记需要移除的项
|
||||
rearViewList.value.forEach((item: any, index: number) => {
|
||||
if (item.unique_no && !newUniqueNos.has(item.unique_no)) {
|
||||
toRemove.push(index)
|
||||
}
|
||||
})
|
||||
|
||||
// 从后往前删除,避免索引变化
|
||||
toRemove.reverse().forEach((index: number) => {
|
||||
rearViewList.value.splice(index, 1)
|
||||
})
|
||||
|
||||
// 更新或添加新项
|
||||
newList.forEach((newItem: any) => {
|
||||
if (newItem.unique_no) {
|
||||
const existing = existingMap.get(newItem.unique_no)
|
||||
if (existing) {
|
||||
// 更新现有项,使用 Object.assign 确保响应式更新
|
||||
const existingItem = existing.item
|
||||
const hasStatusChange = existingItem.status !== newItem.status
|
||||
const hasUrlChange = existingItem.url !== newItem.url
|
||||
|
||||
if (hasStatusChange || hasUrlChange) {
|
||||
// 只更新变化的属性,避免不必要的响应式触发
|
||||
if (hasStatusChange) {
|
||||
existingItem.status = newItem.status
|
||||
}
|
||||
if (hasUrlChange) {
|
||||
existingItem.url = newItem.url
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 新项,添加到列表末尾
|
||||
rearViewList.value.push(newItem)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 保持选中状态
|
||||
await nextTick()
|
||||
if (selectedRearView.value && selectedRearView.value.unique_no) {
|
||||
const foundIndex = rearViewList.value.findIndex(
|
||||
(item: any) => item.unique_no === selectedRearView.value?.unique_no
|
||||
)
|
||||
if (foundIndex !== -1) {
|
||||
selectedRearViewIndex.value = foundIndex
|
||||
selectedRearView.value = rearViewList.value[foundIndex]
|
||||
} else {
|
||||
selectedRearViewIndex.value = null
|
||||
selectedRearView.value = null
|
||||
}
|
||||
} else if (selectedRearViewIndex.value !== null && rearViewList.value[selectedRearViewIndex.value]) {
|
||||
selectedRearView.value = rearViewList.value[selectedRearViewIndex.value]
|
||||
}
|
||||
}
|
||||
|
||||
// 启动轮询
|
||||
function startRearViewPolling() {
|
||||
// 先清除之前的轮询
|
||||
stopRearViewPolling()
|
||||
|
||||
// 设置轮询标志
|
||||
isRearViewPolling.value = true
|
||||
|
||||
// 每10秒轮询一次
|
||||
rearViewPollingTimer.value = setInterval(async () => {
|
||||
// 确保轮询标志仍然为 true(防止被其他操作中断)
|
||||
if (isRearViewPolling.value && !isLoadingRearViewList.value) {
|
||||
// 轮询请求,传入 true 标识
|
||||
await loadRearViewList(true)
|
||||
|
||||
// 检查是否还有生成中的项,如果没有则停止轮询
|
||||
const hasGenerating = rearViewList.value.some((item: any) => item.status === 0)
|
||||
if (!hasGenerating) {
|
||||
stopRearViewPolling()
|
||||
}
|
||||
}
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
// 停止轮询
|
||||
function stopRearViewPolling() {
|
||||
isRearViewPolling.value = false
|
||||
if (rearViewPollingTimer.value) {
|
||||
clearInterval(rearViewPollingTimer.value)
|
||||
rearViewPollingTimer.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
||||
// code 的清除已在 main.ts 中统一处理,这里只处理页面特定的清理逻辑
|
||||
// 清除轮询和进度条计时器
|
||||
clearInterval(timer.value)
|
||||
clearInterval(progressTimer.value)
|
||||
stopRearViewPolling()
|
||||
// userId 的清除由 main.ts 统一处理
|
||||
}
|
||||
|
||||
@@ -1018,11 +1517,31 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer.value)
|
||||
clearInterval(progressTimer.value)
|
||||
stopRearViewPolling()
|
||||
})
|
||||
|
||||
const searchResult = ref([])
|
||||
|
||||
// 监听 isViews 变化,如果变为 1 则加载后视图列表
|
||||
watch(() => isViews.value, (val) => {
|
||||
if (val === 1 && pid.value) {
|
||||
loadRearViewList()
|
||||
} else {
|
||||
stopRearViewPolling()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
/* 后视图标签页样式 */
|
||||
.rear-view-record-section .el-tabs__item.is-active,
|
||||
.rear-view-record-section .el-tabs__item:hover {
|
||||
color: #07c160!important;
|
||||
}
|
||||
|
||||
.rear-view-record-section .el-tabs__active-bar {
|
||||
background-color: #07c160!important;
|
||||
}
|
||||
.preview-container {
|
||||
min-height: 100vh;
|
||||
font-family: 'PingFang SC', Arial, sans-serif;
|
||||
@@ -1481,4 +2000,143 @@ const searchResult = ref([])
|
||||
.van-address-edit {
|
||||
padding: 0!important;
|
||||
}
|
||||
|
||||
/* 后视图相关样式 */
|
||||
.rear-view-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rear-view-record-section {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.rear-view-image-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 10px 0;
|
||||
max-height: 435px; /* 两行图片高度: 200px * 2 + 15px (gap) + 20px (padding) */
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.rear-view-image-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.rear-view-image-item:hover {
|
||||
border-color: #07c160;
|
||||
}
|
||||
|
||||
.rear-view-image-item.active {
|
||||
border-color: #67c23a;
|
||||
box-shadow: 0 0 10px rgba(103, 194, 58, 0.3);
|
||||
}
|
||||
|
||||
.rear-view-image-item.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f7fa;
|
||||
border: 2px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.rear-view-image-item .loading-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f5f7fa;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.rear-view-image-item .loading-placeholder .el-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.rear-view-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rear-view-image-actions {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
padding: 10px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.rear-view-image-item:hover .rear-view-image-actions,
|
||||
.rear-view-image-item.active .rear-view-image-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.rear-view-requirements-content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.rear-view-requirements-content .requirements-text {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.rear-view-requirements-content .requirements-text p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.preset-prompt-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.preset-prompt-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.preset-prompt-item:hover {
|
||||
border-color: #07c160;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.preset-prompt-item .prompt-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.preset-prompt-item .prompt-content {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.empty-tips {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||