forked from natuka/web.puabadge.com
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1457 lines
38 KiB
1457 lines
38 KiB
<template> |
|
<div class="preview-container"> |
|
<view class="backstyle_1"></view> |
|
<view class="backstyle_2"></view> |
|
<div class="header" @click="goBack"> |
|
<span class="back-icon"><van-icon name="arrow-left" size="16px" /> 效果预览</span> |
|
</div> |
|
<div class="step-container"> |
|
<div class="step-item"> |
|
<div class="step-content"> |
|
<div class="step-title">正面照片</div> |
|
<div class="step-desc">清晰的正面照片</div> |
|
</div> |
|
</div> |
|
<div class="step-item active"> |
|
<div class="step-num">2</div> |
|
<div class="step-content"> |
|
<div class="step-title">确认下单</div> |
|
<div class="step-desc">选择一个你喜欢的效果图下单</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div v-if="!imageUrl" class="progress-section"> |
|
<div class="progress-bar-bg"> |
|
<div class="progress-bar-fg" :style="{ width: progress + '%' }"></div> |
|
</div> |
|
<div class="progress-text">{{progressText}} {{progress}}%</div> |
|
<div class="progress-desc">总计大约需要90秒,请耐心等待...</div> |
|
</div> |
|
<div v-else class="progress-section-picture" style="width: 320px;height: 320px;"> |
|
<div class="progress-section-img" style="width: 320px;height: 320px;"> |
|
<img v-if="shapeImage" class="box1-back-image" :src="shapeImage" style="width: 320px;height: 320px;" alt=""> |
|
<div class="box1-front-box" :style="getStyle"> |
|
<img class="box1-front-image" :src="imageUrl" alt=""> |
|
<!-- <div class="box1-round-shadow" v-if="shapeId == 1 && shapeImage == ''" style="width: 320px;height: 320px;"></div> --> |
|
</div> |
|
<div class="shape-text" :style="shapeTextStyle" v-if="shapeText">{{shapeText}}</div> |
|
</div> |
|
</div> |
|
<div class="image-list-box" v-if="imageList.length > 1"> |
|
<div class="image-list-item" v-for="item in imageList" :key="item.key"> |
|
<img v-if="item.status == 1" class="image-list-item-img" :class="{ imgActive: item.key == imgKey }" :src="item.origin_url" alt="" @click="changeImage(item)"> |
|
<div v-else-if="item.status == 0" class="image-list-item-loading"> |
|
设计中 |
|
</div> |
|
<div v-else-if="item.status == 2" class="image-list-item-loading"> |
|
设计失败 |
|
</div> |
|
</div> |
|
</div> |
|
<div class="info-section"> |
|
<div class="info-item"> |
|
<div class="info-title" v-if="typeId == 1">{{ prodId == 7 ? '人物立体徽章' : '宠物立体徽章' }}</div> |
|
<div class="info-title" v-if="typeId == 2">{{ prodId == 7 ? '人物浮雕相框' : '宠物浮雕相框' }}</div> |
|
<div class="info-title" v-if="typeId == 3">{{ prodId == 7 ? '人物3D冰箱贴' : '宠物3D冰箱贴' }}</div> |
|
<div class="info-content">产品类型</div> |
|
</div> |
|
<div class="info-item"> |
|
<div class="info-title">3D全彩打印</div> |
|
<div class="info-content">工艺</div> |
|
</div> |
|
<div class="info-item"> |
|
<div class="info-title">ID</div> |
|
<div class="info-content">{{ pid }}</div> |
|
</div> |
|
</div> |
|
<div class="shape-body"> |
|
<div class="shape-type" v-if="imageUrl && typeId == 3"> |
|
<div class="shape-type-item" :class="{ 'shape-active': item.id == shapeId }" v-for="item in shapeList" :key="item.id" @click="shapeChange(item)">{{item.name}}</div> |
|
</div> |
|
<div class="shape-box" v-if="imageUrl && typeId != 3"> |
|
<block v-for="item in shapeList" :key="item.id"> |
|
<div class="shape-item" @click="shapeChange(item)"> |
|
<div class="shape-item-list"> |
|
<img class="shape-item-image" :class="{ 'shape-item-image-round': item.id == 1 || item.id == 2 }" :src="item.cover_path || imageUrl"/> |
|
<van-icon v-if="item.id == shapeId" class="shape-icon" color="#15CF5F" name="checked" size="18px" /> |
|
</div> |
|
<div class="shape-item-text">{{item.name}}</div> |
|
</div> |
|
</block> |
|
</div> |
|
<div class="shape-box-input" v-if="imageUrl && custom_switch == 1"> |
|
<input class="shape-box-input-text" type="text" placeholder="请输入文字" v-model="shapeText" @change="changeShapeText" @input="changeShapeText" @onBlur="changeShapeText" /> |
|
</div> |
|
</div> |
|
<!-- <div class="info-desc"> |
|
下单后预计需要7天完成生产发货。<br> |
|
实物3D打印色彩与图片有合理色差,如对产品有疑问,可以联系客服咨询。 |
|
</div> --> |
|
<div style="padding: 0 16px;"> |
|
<van-divider /> |
|
</div> |
|
<div class="order-section" v-if="trialCode == false && imageUrl"> |
|
<div class="order-title">订购数量</div> |
|
<block v-for="item in sizeList" :key="item.id"> |
|
<block> |
|
<div class="order-item"> |
|
<span class="order-size">{{ item.size }}</span> |
|
<span class="order-free">(剩余兑换:{{ item.remaining }})</span> |
|
<div class="order-ctrl"> |
|
<van-stepper v-model="item.count" :min="0" :max="item.remaining" @change="changeValue" /> |
|
</div> |
|
</div> |
|
</block> |
|
<!-- <block v-if="orderStat.use_type == 2"> |
|
<div class="order-item"> |
|
<span class="order-size">{{ item.size }}</span> |
|
<span class="order-free">(剩余兑换:{{ item.remaining }})</span> |
|
<div class="order-ctrl"> |
|
<van-stepper v-model="item.count" :min="0" :max="item.remaining" @change="changeValue" /> |
|
</div> |
|
</div> |
|
</block> --> |
|
</block> |
|
</div> |
|
<div class="address-box" v-if="orderStat.use_type == 2 && imageUrl && code == 'ACAYOAAC3PFCPO3CD6DVM1'"> |
|
<div class="order-title" style="padding: 0 16px;">地址识别</div> |
|
<div class="address-item"> |
|
<van-field |
|
v-model="addressInput" |
|
label="" |
|
placeholder="请输入姓名、手机号、详细地址(如:张三 13800138000 广东省深圳市南山区科技园1号)" |
|
clearable |
|
right-icon="search" |
|
> |
|
<template #button> |
|
<van-button size="small" type="primary" @click="handleAddressInput" style="margin-left: 8px;">自动识别</van-button> |
|
</template> |
|
</van-field> |
|
</div> |
|
<div class="address-item" style="padding: 0px 16px 16px 16px;" v-if="contact_name && contact_mobile && province_name && city_name && county_name && address"> |
|
<div style="font-size: 16px;color: #387aff;text-align: left;margin-bottom: 8px;">识别结果</div> |
|
<div style="font-size: 14px;text-align: left;"> |
|
{{ contact_name }} {{ contact_mobile }} {{ province_name }} {{ city_name }} {{ county_name }} {{ address }} |
|
</div> |
|
</div> |
|
</div> |
|
<div class="address-box" v-if="orderStat.use_type == 2 && imageUrl"> |
|
<div class="order-title" style="padding: 0 16px;">收货地址</div> |
|
<div class="address-item"> |
|
<van-address-edit |
|
:area-list="areaList" |
|
:tel-validator="true" |
|
tel-maxlength="11" |
|
show-delete |
|
show-search-result |
|
:search-result="searchResult" |
|
:area-columns-placeholder="['请选择', '请选择', '请选择']" |
|
@change="nameTelChange" |
|
@change-area="changeArea" |
|
@change-detail="onChangeDetail" |
|
/> |
|
</div> |
|
</div> |
|
<div style="height: 130px;"></div> |
|
<div class="confirm-box"> |
|
<div class="action-section"> |
|
<button v-if="trialCode == false" @click="sureReload" :disabled="flag < 1" class="action-btn"><img class="action-img" src="@/assets/badge/reload.png" alt=""> 再次生成</button> |
|
<button @click="compare" :disabled="flag < 1" class="action-btn"><img class="action-img" src="@/assets/badge/duibi.png" alt=""> 前后对比</button> |
|
<button @click="save" :disabled="flag < 1" class="action-btn"><img class="action-img" src="@/assets/badge/down.png" alt=""> 保存图片</button> |
|
</div> |
|
<div class="btn-box" v-if="trialCode == false"> |
|
<button @click="confirm" :disabled="flag < 1" class="confirm-btn">确认选择</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<van-action-sheet v-model:show="showCompare" title="前后对比" :close-on-click-overlay="true" closeable> |
|
<div style="padding: 16px;"> |
|
<div style="display: flex; align-items: center; justify-content: center; gap: 20px;"> |
|
<div style="text-align: center;"> |
|
<img :src="compareList.origin_path" alt="原图" style="max-width: 120px; max-height: 120px; border-radius: 8px;"> |
|
</div> |
|
<div style="font-size: 24px; color: #333;"> |
|
<van-icon name="arrow" size="20px" /> |
|
</div> |
|
<div style="text-align: center;"> |
|
<img :src="imageUrl" alt="效果图" style="width: 120px; height: 120px; border-radius: 8px;"> |
|
</div> |
|
</div> |
|
<div style="text-align: center; margin-top: 16px; color: #999; font-size: 12px;"> |
|
温馨提示:<br/> |
|
AI设计的徽章效果图是根据用户提供的照片机器学习生成的,<br/> |
|
多试几次就能找到你满意的徽章~ |
|
</div> |
|
</div> |
|
</van-action-sheet> |
|
|
|
<div v-if="showPreview"> |
|
<div class="preview-mask" @click="showPreview = false"> |
|
<img :src="imageWater" alt="徽章预览" /> |
|
<p>长按图片 → 保存到相册</p> |
|
</div> |
|
</div> |
|
</template> |
|
<script setup lang="ts"> |
|
import { onMounted, ref, onUnmounted } from 'vue'; |
|
import { useRouter, useRoute } from 'vue-router'; |
|
import { showSuccessToast, showToast, showNotify, showConfirmDialog, AddressEdit, Area } from 'vant'; |
|
import { localStorage } from '@/utils/local-storage' |
|
import * as badgeApi from '@/api/badge' |
|
import { areaList } from '@/utils/area' |
|
const router = useRouter(); |
|
const value = ref(0); |
|
const showReload = ref(false) |
|
const showCompare = ref(false) |
|
const showBgCompare = ref(false) |
|
const currentIndex = ref(0) |
|
const imageList = ref([]) |
|
const imageWater = ref('') |
|
|
|
const code = localStorage.get('code') |
|
|
|
function compare() { |
|
showCompare.value = true |
|
} |
|
|
|
function sureReload() { |
|
console.log('orderStat', orderStat.value) |
|
if (orderStat.value.remain_count <= 0) { |
|
showToast('当前次数已用完,请重新购买') |
|
return |
|
} |
|
isPreview.value = false |
|
showConfirmDialog({ |
|
title: '再次生成', |
|
message: |
|
'请确认是否重新生成。', |
|
}) |
|
.then(() => { |
|
group.value = newGroup.value |
|
const params = { |
|
pid: pid.value, |
|
group: group.value, |
|
prod_id: prodId.value, |
|
type_id: typeId.value |
|
} |
|
badgeApi.putModeling(params).then((res: any) => { |
|
console.log('putModeling', res) |
|
imageUrl.value = ''; |
|
imageList.value = [] |
|
imgKey.value = ''; |
|
createLog() |
|
}).catch((err) => { |
|
console.log('putModeling', err) |
|
showReload.value = false |
|
showToast({ |
|
message: err.message, |
|
duration: 2000, |
|
}) |
|
}).finally(() => { |
|
showReload.value = false |
|
}) |
|
}) |
|
.catch(() => { |
|
showReload.value = false |
|
}); |
|
|
|
} |
|
|
|
const imgKey = ref(101) |
|
const frameUrl = ref('') |
|
const changeImage = (item: any) => { |
|
console.log('changeImage', item) |
|
imgKey.value = item.key |
|
imageUrl.value = shapeId.value == 17 || shapeId.value == 18 ? item.frame_url : item.origin_url |
|
frameUrl.value = item.frame_url |
|
imageWater.value = item.url |
|
} |
|
|
|
const createLog = () => { |
|
badgeApi.createLog({ |
|
pid: pid.value, |
|
group: group.value, |
|
prod_id: prodId.value, |
|
type_id: typeId.value |
|
}).then((res: any) => { |
|
console.log('createLog', res) |
|
showReload.value = false |
|
progressTimer.value = null |
|
getImageList() |
|
progressList() |
|
getCompareImage() |
|
timer.value = setInterval(() => { |
|
getImageList() |
|
}, 10000) |
|
}).catch((err) => { |
|
console.log('createLog', err) |
|
showToast({ |
|
message: err.message, |
|
duration: 2000, |
|
}) |
|
}).finally((err) => { |
|
showReload.value = false |
|
}) |
|
} |
|
|
|
const showPreview = ref(false) |
|
function save() { |
|
showPreview.value = true; |
|
} |
|
const compareList = ref({}) |
|
const style_name = ref('') |
|
function getCompareImage() { |
|
badgeApi.getCompareImage({ |
|
pid: pid.value, |
|
group: group.value, |
|
prod_id: prodId.value, |
|
type_id: typeId.value |
|
}).then((res: any) => { |
|
console.log('getCompareImage', res) |
|
compareList.value = res |
|
style_name.value = res.list[0].kind_name || res.kind_name |
|
}).catch((err) => { |
|
console.log('getCompareImage', err) |
|
showToast({ |
|
message: err.message, |
|
duration: 2000, |
|
}) |
|
}).finally((err) => { |
|
|
|
}) |
|
} |
|
function goBack() { |
|
router.back() |
|
} |
|
|
|
const sizeList = ref([]) |
|
const shapeItem = ref({}) |
|
const getSizeList = () => { |
|
badgeApi.getOrderPrice({}).then((res: any) => { |
|
console.log('getSizeList', res); |
|
sizeList.value = res |
|
getShapeList() |
|
}) |
|
} |
|
|
|
const contact_name = ref('') |
|
const contact_mobile = ref('') |
|
const province_id = ref(0) |
|
const city_id = ref(0) |
|
const county_id = ref(0) |
|
const province_name = ref('') |
|
const city_name = ref('') |
|
const county_name = ref('') |
|
const address = ref('') |
|
const nameTelChange = (res: any) => { |
|
console.log('nameTelChange', res) |
|
if (res.key == 'name') { |
|
contact_name.value = res.value |
|
} else { |
|
contact_mobile.value = res.value |
|
} |
|
} |
|
|
|
const changeArea = (value: any) => { |
|
console.log('changeArea', value) |
|
province_id.value = value[0].value |
|
city_id.value = value[1].value |
|
county_id.value = value[2].value |
|
province_name.value = value[0].text |
|
city_name.value = value[1].text |
|
county_name.value = value[2].text |
|
} |
|
|
|
const onChangeDetail = (value: any) => { |
|
console.log('onChangeDetail', value) |
|
address.value = value |
|
} |
|
|
|
const payAmount = ref(0) |
|
const changeValue = (value: number) => { |
|
console.log(value) |
|
payAmount.value = value |
|
} |
|
|
|
const addressInput = ref('') |
|
const handleAddressInput = () => { |
|
console.log('handleAddressInput', addressInput.value) |
|
const text = addressInput.value; |
|
if (!text) return; |
|
// 识别手机号 |
|
const phoneRegex = /(1[3-9]\d{9})/g; |
|
const phoneMatch = phoneRegex.exec(text); |
|
if (phoneMatch) { |
|
contact_mobile.value = phoneMatch[0]; |
|
} |
|
// 简单识别姓名(2-4个中文字符) |
|
const nameRegex = /([\u4e00-\u9fa5]{2,4})/g; |
|
const nameMatches = text.match(nameRegex); |
|
if (nameMatches && nameMatches.length > 0) { |
|
// 假设第一个匹配的中文名是姓名 |
|
contact_name.value = nameMatches[0]; |
|
} |
|
console.log('contact_name', contact_name.value) |
|
console.log('contact_mobile', contact_mobile.value) |
|
// 识别省份 |
|
let provinceFound = ''; |
|
for (const [provinceId, provinceName] of Object.entries(areaList.province_list)) { |
|
if (text.includes(provinceName)) { |
|
provinceFound = provinceName; |
|
console.log('识别到省份ID:', provinceId); |
|
province_id.value = provinceId; |
|
break; |
|
} |
|
} |
|
console.log('province_id', province_id.value) |
|
console.log('provinceFound', provinceFound) |
|
province_name.value = provinceFound; |
|
// 识别城市 |
|
let cityFound = ''; |
|
for (const [cityId, cityName] of Object.entries(areaList.city_list)) { |
|
if (text.includes(cityName) && cityId !== province_id.value) { |
|
cityFound = cityName; |
|
city_id.value = cityId; |
|
break; |
|
} |
|
} |
|
console.log('cityFound', cityFound) |
|
city_name.value = cityFound; |
|
// 提取详细地址(从文本中移除已识别的部分) |
|
let detail = text |
|
.replace(contact_name.value, '') |
|
.replace(contact_mobile.value, '') |
|
.replace(province_name.value, '') |
|
.replace(city_name.value, '') |
|
.trim(); |
|
// 移除可能的区级信息(简单处理) |
|
if (detail.includes('区') || detail.includes('县')) { |
|
const districtMatch = detail.match(/(.+?(区|县))/); |
|
if (districtMatch) { |
|
// 遍历区县列表,查找匹配的区县名,获取对应的区id |
|
let foundCountyId = ''; |
|
for (const [cid, cname] of Object.entries(areaList.county_list)) { |
|
if (districtMatch[0].includes(cname)) { |
|
foundCountyId = cid; |
|
county_id.value = cid; |
|
county_name.value = cname; |
|
break; |
|
} |
|
} |
|
// 如果没有在区县列表中找到,仍然保留原有逻辑 |
|
if (!foundCountyId) { |
|
county_id.value = 0; |
|
county_name.value = ''; |
|
} |
|
detail = detail.replace(county_name.value, '').trim(); |
|
} |
|
} |
|
console.log('county_id', county_id.value) |
|
console.log('county_name', county_name.value) |
|
console.log('detail', detail) |
|
address.value = detail; |
|
} |
|
|
|
const key = ref(0) |
|
const loading = ref(false) |
|
const confirm = () => { |
|
if (loading.value) return |
|
console.log('confirm') |
|
if (payAmount.value <= 0) { |
|
showToast('请先选择下单数量') |
|
return |
|
} |
|
if (orderStat.value.use_type == 2 && !contact_name.value) { |
|
showToast('请先填写收货人信息') |
|
return |
|
} |
|
if (orderStat.value.use_type == 2 && !contact_mobile.value) { |
|
showToast('请先填写收货人手机号') |
|
return |
|
} |
|
if (orderStat.value.use_type == 2 && !province_id.value) { |
|
showToast('请先选择地区') |
|
return |
|
} |
|
if (orderStat.value.use_type == 2 && !address.value) { |
|
showToast('请先填写详细地址') |
|
return |
|
} |
|
getPosition() |
|
loading.value = true |
|
showConfirmDialog({ |
|
title: '确认下单', |
|
message: |
|
'请再次确认是否选择这个模型下单,下单后预计需要10天完成生产发货。如对产品有疑问,可以联系客服咨询。', |
|
}) |
|
.then(() => { |
|
const parms = { |
|
pid: pid.value, |
|
key: imgKey.value, |
|
pay_amount: payAmount.value, |
|
products: sizeList.value, |
|
prod_id: prodId.value, |
|
shape_id: shapeId.value, |
|
custom_text: shapeText.value |
|
} |
|
if (orderStat.value.use_type == 2) { |
|
parms.province_id = province_id.value |
|
parms.city_id = city_id.value |
|
parms.county_id = county_id.value |
|
parms.province_name = province_name.value |
|
parms.city_name = city_name.value |
|
parms.county_name = county_name.value |
|
parms.address = address.value |
|
parms.contact_name = contact_name.value |
|
parms.contact_mobile = contact_mobile.value |
|
parms.addr_id = 0 |
|
parms.country_id = 45 |
|
} |
|
console.log('parms', parms) |
|
badgeApi.creatOrder(parms).then((res: any) => { |
|
console.log('creatOrder', res) |
|
showSuccessToast({ |
|
message: '下单成功', |
|
duration: 2000, |
|
}) |
|
router.push({ |
|
path: '/badge/myOrder' |
|
}) |
|
loading.value = false |
|
}).catch((err) => { |
|
console.log('creatOrder', err) |
|
loading.value = false |
|
showToast({ |
|
message: err.message, |
|
duration: 2000, |
|
}) |
|
}).finally(() => { |
|
loading.value = false |
|
}) |
|
}) |
|
.catch(() => { |
|
loading.value = false |
|
// on cancel |
|
}); |
|
|
|
} |
|
|
|
const getPosition = () => { |
|
badgeApi.composite({ |
|
pid: pid.value, |
|
num: imgKey.value, |
|
custom_text: shapeText.value, |
|
shape_id: shapeId.value, |
|
}).then((res: any) => { |
|
console.log('getPosition', res) |
|
}).catch((err) => { |
|
console.log('getPosition', err) |
|
}).finally((err) => { |
|
|
|
}) |
|
} |
|
|
|
const imageUrl = ref('') |
|
const originUrl = ref('') |
|
const group = ref(1); |
|
// 轮询获取图片 |
|
const flag = ref(1) |
|
const timer = ref() |
|
const progress = ref(0) |
|
const progressTimer = ref() |
|
const progressText = ref('') |
|
const progressList = () => { |
|
// 启动进度条计时器 |
|
progress.value = 0 |
|
if (!progressTimer.value) { |
|
progressTimer.value = setInterval(() => { |
|
if (progress.value < 90) { |
|
progress.value += 1 |
|
if (progress.value < 25) { |
|
progressText.value = '照片数据分析中' |
|
} else if (progress.value < 50) { |
|
progressText.value = '提炼图像关键词' |
|
} else if (progress.value < 75) { |
|
progressText.value = '图像生成中' |
|
} else { |
|
progressText.value = '图像后处理与优化' |
|
} |
|
} |
|
}, 300) |
|
} |
|
} |
|
|
|
const newGroup = ref(0) |
|
const isPreview = ref(false) |
|
const getImageList = () => { |
|
badgeApi.getImageList({ |
|
pid: pid.value, |
|
group: group.value, |
|
prod_id: prodId.value |
|
}).then((res: any) => { |
|
console.log('getImageList', res) |
|
const data = res || [] |
|
flag.value = data.flag |
|
prodId.value = data.prod_id |
|
if (data.flag === 1) { |
|
newGroup.value = data.next_group |
|
|
|
nextTick(() => { |
|
// 更新图片列表 |
|
const newList = data.list.map(item => ({ |
|
...item, |
|
isNew: true |
|
})) |
|
|
|
// 找到第一个状态为1(已生成)的图片 |
|
const firstGeneratedImage = newList.find(item => item.status === 1) |
|
|
|
// 如果有已生成的图片且还未设置预览图,则显示第一个生成的图片 |
|
if (firstGeneratedImage && !isPreview.value) { |
|
originUrl.value = firstGeneratedImage.origin_url |
|
imageUrl.value = firstGeneratedImage.origin_url |
|
frameUrl.value = firstGeneratedImage.frame_url |
|
imageWater.value = firstGeneratedImage.url |
|
imgKey.value = firstGeneratedImage.key |
|
isPreview.value = true |
|
} |
|
|
|
// 合并新旧列表,保持原有顺序 |
|
const mergedList = imageList.value.map(item => { |
|
const newItem = newList.find(n => n.key === item.key) |
|
return newItem || item |
|
}) |
|
|
|
// 添加新图片 |
|
newList.forEach(newItem => { |
|
if (!mergedList.find(item => item.key === newItem.key)) { |
|
mergedList.push(newItem) |
|
} |
|
}) |
|
|
|
// 更新图片列表 |
|
imageList.value = mergedList |
|
|
|
// 如果已有选中图片,保持显示当前选中的图片 |
|
console.log('imgKey.value', imgKey.value) |
|
if (imgKey.value) { |
|
const currentImage = mergedList.filter((item: any) => item.key === imgKey.value)[0] |
|
console.log('currentImage', currentImage) |
|
if (currentImage && currentImage.status === 1) { |
|
originUrl.value = currentImage.origin_url |
|
imageUrl.value = currentImage.origin_url |
|
frameUrl.value = currentImage.frame_url |
|
imageWater.value = currentImage.url |
|
isPreview.value = true |
|
} else { |
|
originUrl.value = currentImage.origin_url |
|
imageUrl.value = currentImage.origin_url |
|
frameUrl.value = currentImage.frame_url |
|
imageWater.value = currentImage.url |
|
isPreview.value = true |
|
} |
|
} |
|
}) |
|
} |
|
if (data.flag === 2) { |
|
newGroup.value = data.next_group |
|
// 清除轮询和进度条计时器 |
|
clearInterval(timer.value) |
|
clearInterval(progressTimer.value) |
|
|
|
// 进度条到100% |
|
progress.value = 100 |
|
|
|
// 显示图片 |
|
imageList.value = data.list |
|
if (imgKey.value) { |
|
originUrl.value = data.list.filter((item: any) => item.key == imgKey.value)[0].origin_url |
|
imageUrl.value = data.list.filter((item: any) => item.key == imgKey.value)[0].origin_url |
|
frameUrl.value = data.list.filter((item: any) => item.key == imgKey.value)[0].frame_url |
|
imageWater.value = data.list.filter((item: any) => item.key == imgKey.value)[0].url |
|
} else { |
|
originUrl.value = data.list[0].origin_url |
|
imageUrl.value = data.list[0].origin_url |
|
frameUrl.value = data.list[0].frame_url |
|
imageWater.value = data.list[0].url |
|
imgKey.value = data.list[0].key |
|
} |
|
localStorage.remove('userId') |
|
} |
|
}).catch((err) => { |
|
// 清除轮询和进度条计时器 |
|
// clearInterval(timer.value) |
|
// clearInterval(progressTimer.value) |
|
// progress.value = 0 |
|
showToast({ |
|
message: err.message, |
|
duration: 2000, |
|
}) |
|
}).finally((err) => { |
|
|
|
}) |
|
} |
|
|
|
const shapeId = ref(0) |
|
const shapeImage = ref('') |
|
|
|
const shapeList = ref([]) |
|
const custom_switch = ref(0) |
|
const limitCount = ref(0) |
|
const getShapeList = () => { |
|
badgeApi.getShapeList({ |
|
prod_id: prodId.value, |
|
type_id: orderStat.value.type_id |
|
}).then((res: any) => { |
|
console.log('getShapeList', res) |
|
shapeList.value = res.list |
|
shapeId.value = res.list[0].id |
|
shapeImage.value = res.list[0]?.frame_path |
|
custom_switch.value = res.list[0]?.custom_switch |
|
limitCount.value = res.list[0]?.text_limit_max |
|
ImageShow(res.list[0]) |
|
if (custom_switch.value == 1) { |
|
textShow(res.list[0]) |
|
} |
|
}).catch((err) => { |
|
console.log('getShapeList', err) |
|
}).finally((err) => { |
|
|
|
}) |
|
} |
|
|
|
const getStyle = ref('') |
|
const ImageShow = (item: any) => { |
|
const scale = 0.6; |
|
const img = new Image() |
|
img.src = item.frame_path |
|
img.onload = () => { |
|
console.log('img', img) |
|
const ratioWidth = 320 / img.width; |
|
const ratioHeight = 320 / img.height; |
|
const x = item.axisx * ratioWidth * scale; |
|
const y = item.axisy * ratioHeight * scale; |
|
const path_width = item.width * ratioWidth * scale; |
|
const path_height = item.height * ratioHeight * scale; |
|
getStyle.value = `left: ${x}px;top: ${y}px;width: ${path_width}px;height: ${path_height}px;` |
|
} |
|
} |
|
|
|
const shapeTextStyle = ref('') |
|
const textShow = (item: any) => { |
|
const scale = 0.6; |
|
const img = new Image() |
|
img.src = item.frame_path |
|
img.onload = () => { |
|
console.log('img', img) |
|
const ratioWidth = 320 / img.width; |
|
const ratioHeight = 320 / img.height; |
|
const x = item.text_axisx * ratioWidth * scale; |
|
const y = item.text_axisy * ratioHeight * scale; |
|
const path_width = item.text_width * ratioWidth * scale; |
|
const path_height = item.text_height * ratioHeight * scale; |
|
const text_size = item.font_size * Math.min(ratioWidth * scale, ratioHeight * scale); |
|
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};` |
|
} |
|
} |
|
|
|
const shapeText = ref('') |
|
const changeShapeText = (e: any) => { |
|
console.log('changeShapeText', e.target.value) |
|
// if (!validateInput(e.target.value)) { |
|
// showToast('输入的文字不能包含特殊符号!') |
|
// shapeText.value = e.target.value.replace(/[^a-zA-Z0-9\u4e00-\u9fa5\s]/g, ''); |
|
// return; |
|
// } |
|
handleInput(e.target.value) |
|
} |
|
|
|
const validateInput = (input: string) => { |
|
// 正则说明:只允许字母(大小写)、数字、中文和空格 |
|
const regex = /^[a-zA-Z0-9\u4e00-\u9fa5\s]+$/; |
|
return regex.test(input); |
|
} |
|
|
|
const handleInput = (value: string) => { |
|
let byteLength = calculateByteLength(value); |
|
console.log('byteLength', byteLength, limitCount.value) |
|
if (byteLength > limitCount.value) { |
|
let validValue = ''; |
|
let currentBytes = 0; |
|
for (let i = 0; i < value.length; i++) { |
|
const char = value.charAt(i); |
|
const code = char.charCodeAt(0); |
|
let charBytes = 0; |
|
if (code === 32) { |
|
charBytes = 1; |
|
} else { |
|
charBytes = code > 255 ? 2 : 1; |
|
} |
|
if (currentBytes + charBytes <= limitCount.value) { |
|
validValue += char; |
|
currentBytes += charBytes; |
|
} else { |
|
break; |
|
} |
|
} |
|
shapeText.value = validValue; |
|
return; |
|
} |
|
shapeText.value = value; |
|
} |
|
|
|
const calculateByteLength = (str: string) => { |
|
let len = 0; |
|
for (let i = 0; i < str.length; i++) { |
|
const code = str.charCodeAt(i); |
|
if (code === 32) { |
|
len += 1; |
|
} else { |
|
len += code > 255 ? 2 : 1; |
|
} |
|
} |
|
return len; |
|
} |
|
|
|
const shapeChange = (item: any) => { |
|
console.log('shapeChange', item) |
|
sizeList.value.map((item: any) => { |
|
if (item.shape_id !== item.id) { |
|
item.count = 0 |
|
} |
|
}) |
|
shapeText.value = ''; |
|
shapeId.value = item.id |
|
shapeImage.value = item.frame_path |
|
custom_switch.value = item.custom_switch |
|
limitCount.value = item.text_limit_max |
|
if (typeId.value == 3) { |
|
imageUrl.value = item.id == 15 || item.id == 16 ? originUrl.value : frameUrl.value |
|
} else { |
|
imageUrl.value = originUrl.value |
|
} |
|
ImageShow(item) |
|
if (custom_switch.value == 1) { |
|
textShow(item) |
|
} |
|
} |
|
|
|
const trialCode = ref(false) |
|
const getTrialCode = () => { |
|
const value = localStorage.get('trialCode') |
|
if (value) { |
|
trialCode.value = true |
|
} else { |
|
getOrderStat() |
|
} |
|
} |
|
|
|
const orderStat = ref({}) |
|
const typeId = ref(0) |
|
const getOrderStat = () => { |
|
badgeApi.getOrderStat({}).then((res: any) => { |
|
console.log('getOrderStat', res) |
|
orderStat.value = res |
|
typeId.value = res.type_id |
|
getSizeList() |
|
}) |
|
} |
|
|
|
function handleBeforeUnload(_event: BeforeUnloadEvent) { |
|
localStorage.remove('code') |
|
localStorage.remove('trialCode') |
|
localStorage.remove('userId') |
|
// 清除轮询和进度条计时器 |
|
clearInterval(timer.value) |
|
clearInterval(progressTimer.value) |
|
} |
|
|
|
const pid = ref(0) |
|
const route = useRoute() |
|
const prodId = ref(0) |
|
onMounted(() => { |
|
pid.value = route.query.pid |
|
group.value = route.query.group |
|
if (route.query.key) { |
|
imgKey.value = route.query.key |
|
} |
|
if (route.query.prod_id) { |
|
prodId.value = route.query.prod_id |
|
} |
|
|
|
// pid.value = 281505; |
|
// group.value = 1; |
|
// prodId.value = 7; |
|
// getTypesList() |
|
getImageList() |
|
progressList() |
|
getCompareImage() |
|
getTrialCode() |
|
timer.value = setInterval(() => { |
|
getImageList() |
|
}, 10000) |
|
window.addEventListener('beforeunload', handleBeforeUnload); |
|
}) |
|
|
|
onUnmounted(() => { |
|
clearInterval(timer.value) |
|
clearInterval(progressTimer.value) |
|
}) |
|
</script> |
|
<style scoped> |
|
.preview-container { |
|
min-height: 100vh; |
|
font-family: 'PingFang SC', Arial, sans-serif; |
|
color: #222; |
|
} |
|
.backstyle_1 { |
|
position: fixed; |
|
left: -110px; |
|
top: -40px; |
|
width: 328px; |
|
height: 216px; |
|
opacity: 0.2; |
|
transform: rotate(-13.72deg); |
|
border-radius: 50%; |
|
/* background: radial-gradient(48.48% 50% at 50% 50%, #EBD4A0 0%, #ebd5a000 81.94%); */ |
|
background-image: radial-gradient( rgba(250, 182, 55, 0.726), #ebd5a09c, #d4cab100); |
|
z-index: -10; |
|
} |
|
.backstyle_2 { |
|
position: fixed; |
|
left: 97px; |
|
top: -95px; |
|
width: 370px; |
|
height: 374px; |
|
opacity: 0.1; |
|
border-radius: 50%; |
|
/* background: radial-gradient(59.73% 50% at 50% 50%, #90F272 0%, #90f27200 84.03%); */ |
|
background-image: radial-gradient( rgb(69, 255, 63), #90f2729a, #90f27200); |
|
z-index: -10; |
|
} |
|
.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; |
|
} |
|
|
|
.step-container { |
|
display: flex; |
|
align-items: flex-start; |
|
padding: 16px 16px 16px 0; |
|
} |
|
.step-item { |
|
display: flex; |
|
align-items: center; |
|
flex: 1; |
|
overflow: hidden; |
|
white-space: nowrap; |
|
text-overflow: ellipsis; |
|
} |
|
.step-item.active { |
|
flex: 2.5; |
|
} |
|
.step-num { |
|
font-size: 48px; |
|
line-height: 1; |
|
position: relative; |
|
margin-right: 8px; |
|
text-shadow: 0 2px 8px #e6f7e6; |
|
color: #CCCCCC; |
|
} |
|
.step-item.active .step-num { |
|
color: #fff; |
|
text-shadow: |
|
1px 1px 0 #000, |
|
-1px -1px 0 #000, |
|
-1px 1px 0 #000, |
|
1px -1px 0 #000, |
|
0 1px 0 #000, |
|
1px 0 0 #000, |
|
0 -1px 0 #000, |
|
-1px 0 0 #000; |
|
} |
|
.step-item.active .step-num::after { |
|
content: ''; |
|
position: absolute; |
|
top: 20px; |
|
right: 0; |
|
width: 15px; |
|
height: 15px; |
|
background-color: #50cf54; |
|
opacity: 0.5; |
|
border-radius: 50%; |
|
} |
|
.step-content { |
|
display: flex; |
|
flex-direction: column; |
|
height: 48px; |
|
} |
|
.step-item.active .step-title { |
|
color: #000; |
|
font-weight: bold; |
|
font-size: 16px; |
|
} |
|
.step-item.active .step-desc { |
|
color: #808080; |
|
font-size: 13px; |
|
} |
|
.step-title { |
|
font-size: 16px; |
|
color: #808080;; |
|
} |
|
.step-desc { |
|
font-size: 12px; |
|
color: #808080; |
|
margin-top: 2px; |
|
} |
|
.progress-section { |
|
margin: 0 auto; |
|
text-align: center; |
|
width: 100vw; |
|
height: 100vw; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
flex-direction: column; |
|
} |
|
.progress-section-picture { |
|
width: 320px; |
|
height: 320px; |
|
position: relative; |
|
margin: 0 auto; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
flex-direction: column; |
|
} |
|
.progress-section-img { |
|
position: relative; |
|
} |
|
.box1-back-image { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
z-index: 2; |
|
object-fit: cover; |
|
border-radius: 10px; |
|
} |
|
.box1-front-box { |
|
position: absolute; |
|
z-index: 1; |
|
} |
|
.box1-front-image { |
|
width: 100%; |
|
height: auto; |
|
border-radius: 10px; |
|
background: rgba(0, 0, 0, 0.3); |
|
} |
|
.box1-round-shadow { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 320px; |
|
height: 320px; |
|
border-radius: 50%; |
|
box-shadow: 2px 2px 4px #00000042,inset -2px -2px 4px #00000048,inset 2px 2px 4px #ffffffb4; |
|
z-index: 10; |
|
} |
|
.box1-round-image { |
|
border-radius: 50%; |
|
} |
|
.progress-bar-bg { |
|
width: 70%; |
|
height: 10px; |
|
background: #eee; |
|
border-radius: 5px; |
|
margin: 0 auto 8px auto; |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
.progress-bar-fg { |
|
height: 100%; |
|
background: #6fdc8c; |
|
border-radius: 5px; |
|
transition: width 0.3s; |
|
} |
|
.progress-text { |
|
font-size: 15px; |
|
color: #222; |
|
margin-bottom: 2px; |
|
} |
|
.progress-desc { |
|
font-size: 12px; |
|
color: #999; |
|
} |
|
.info-section { |
|
display: flex; |
|
justify-content: flex-start; |
|
padding: 16px; |
|
} |
|
.info-item { |
|
flex: 1; |
|
} |
|
.info-title { |
|
font-size: 13px; |
|
color: #222; |
|
font-weight: 600; |
|
margin-bottom: 2px; |
|
} |
|
.info-content { |
|
font-size: 12px; |
|
color: #999; |
|
} |
|
.info-desc { |
|
font-size: 12px; |
|
color: #999; |
|
margin-bottom: 18px; |
|
line-height: 1.5; |
|
text-align: left; |
|
padding: 0 16px; |
|
} |
|
.order-section { |
|
border-radius: 10px; |
|
padding: 0 16px; |
|
margin-bottom: 18px; |
|
} |
|
.order-title { |
|
font-size: 14px; |
|
color: #222; |
|
font-weight: 600; |
|
margin-bottom: 10px; |
|
} |
|
.order-item { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 10px; |
|
} |
|
.order-size { |
|
font-size: 15px; |
|
color: #222; |
|
margin-right: 8px; |
|
} |
|
.order-free { |
|
font-size: 12px; |
|
color: #999; |
|
margin-right: 16px; |
|
} |
|
.order-ctrl { |
|
display: flex; |
|
align-items: center; |
|
margin-left: auto; |
|
} |
|
.order-btn { |
|
width: 26px; |
|
height: 26px; |
|
border: none; |
|
background: #e8e8e8; |
|
color: #bbb; |
|
font-size: 18px; |
|
border-radius: 4px; |
|
margin: 0 4px; |
|
cursor: pointer; |
|
} |
|
.order-btn:disabled { |
|
background: #f2f2f2; |
|
color: #ddd; |
|
cursor: not-allowed; |
|
} |
|
.order-count { |
|
font-size: 15px; |
|
color: #222; |
|
width: 22px; |
|
text-align: center; |
|
} |
|
.confirm-box { |
|
position: fixed; |
|
left: 0; |
|
right: 0; |
|
bottom: 0; |
|
background: #fff; |
|
z-index: 10; |
|
padding: 0 16px 16px 16px; |
|
border-top: 1px solid #e6e6e6; |
|
} |
|
.action-section { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
margin-bottom: 8px; |
|
padding: 0 16px; |
|
} |
|
.action-btn { |
|
flex: 1; |
|
margin: 0 4px; |
|
border: none; |
|
border-radius: 8px; |
|
color: #222; |
|
font-size: 13px; |
|
padding: 8px 0; |
|
cursor: pointer; |
|
transition: background 0.2s; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
background: #fff; |
|
} |
|
.action-img { |
|
width: 20px; |
|
height: 20px; |
|
margin-right: 5px; |
|
} |
|
.btn-box { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
.confirm-btn { |
|
width: 80vw; |
|
background: linear-gradient(90deg, #D1ED8E 0%, #55E668 100%); |
|
color: #000; |
|
font-size: 17px; |
|
font-weight: 600; |
|
border: none; |
|
border-radius: 24px; |
|
padding: 12px 0; |
|
cursor: pointer; |
|
box-shadow: 0 2px 8px rgba(111,220,140,0.08); |
|
letter-spacing: 2px; |
|
} |
|
.confirm-btn:active { |
|
background: linear-gradient(90deg, #5ccf7a 0%, #8fdca0 100%); |
|
} |
|
.style-selection { |
|
padding: 16px; |
|
} |
|
.style-grid { |
|
display: grid; |
|
grid-template-columns: repeat(3, 1fr); |
|
gap: 8px; |
|
} |
|
.style-item { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
cursor: pointer; |
|
position: relative; |
|
width: 28vw; |
|
} |
|
.style-img { |
|
width: 28vw; |
|
height: 28vw; |
|
border-radius: 8px; |
|
object-fit: cover; |
|
background: #f5f5f5; |
|
border: 2px solid transparent; |
|
transition: border 0.2s; |
|
} |
|
.style-item .style-selected { |
|
position: absolute; |
|
right: 4px; |
|
bottom: 25px; |
|
width: 20px; |
|
height: 20px; |
|
border-radius: 50%; |
|
background-color: #fff; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
.style-label { |
|
font-size: 13px; |
|
color: #333; |
|
margin-top: 4px; |
|
} |
|
.confirm-wrapper { |
|
margin-top: 24px; |
|
} |
|
.confirm-button { |
|
width: 100%; |
|
background: linear-gradient(90deg, #D1ED8E 0%, #55E668 100%); |
|
color: #000; |
|
font-size: 17px; |
|
font-weight: 600; |
|
border: none; |
|
border-radius: 24px; |
|
padding: 12px 0; |
|
cursor: pointer; |
|
} |
|
.confirm-button:active { |
|
background: linear-gradient(90deg, #5ccf7a 0%, #8fdca0 100%); |
|
} |
|
.style-item.active .style-img { |
|
border: 2px solid #50cf54; |
|
} |
|
.style-item.active .style-label { |
|
color: #50cf54; |
|
} |
|
.preview-mask { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background: rgba(0, 0, 0, 0.8); |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: center; |
|
z-index: 1000; |
|
} |
|
.preview-mask img { |
|
max-width: 80%; |
|
max-height: 80%; |
|
border-radius: 10px; |
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); |
|
} |
|
.preview-mask p { |
|
margin-top: 16px; |
|
color: #fff; |
|
font-size: 16px; |
|
text-align: center; |
|
} |
|
.progress-image-item-shadow { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 80vw; |
|
height: 80vw; |
|
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); |
|
border-radius: 50%; |
|
} |
|
.image-list-box { |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
gap: 10px; |
|
padding: 16px 16px 0 16px; |
|
} |
|
.image-list-item { |
|
width: 20vw; |
|
height: 20vw; |
|
} |
|
.image-list-item-img { |
|
width: 20vw; |
|
height: 20vw; |
|
border-radius: 12px; |
|
border: 3px solid #fff; |
|
background: rgba(0, 0, 0, 0.3); |
|
} |
|
.image-list-item-img.imgActive { |
|
border: 3px solid #50cf54; |
|
} |
|
.image-list-item-loading { |
|
width: 20vw; |
|
height: 20vw; |
|
border-radius: 12px; |
|
background: #f0f0f0; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 12px; |
|
color: #999; |
|
} |
|
.shape-body { |
|
background: #fff; |
|
border-radius: 8px; |
|
padding: 0 20px; |
|
} |
|
.shape-box { |
|
display: flex; |
|
align-items: center; |
|
flex-wrap: wrap; |
|
justify-content: flex-start; |
|
} |
|
.shape-title { |
|
padding-bottom: 12px; |
|
font-size: 14px; |
|
font-weight: bold; |
|
} |
|
.shape-item { |
|
position: relative; |
|
flex: 0 0 24%; |
|
/* margin-right: calc(4% / 3); */ |
|
margin-bottom: calc(4% / 2); |
|
margin-right: 8px!important; |
|
} |
|
.shape-item:nth-child(4n){ |
|
margin-right: 0; |
|
} |
|
.shape-item:last-child{ |
|
margin-right: auto; |
|
} |
|
.shape-item-image { |
|
width: 20vw; |
|
height: 20vw; |
|
border-radius: 8px; |
|
} |
|
.shape-icon { |
|
position: absolute; |
|
right: 6px; |
|
bottom: 24px; |
|
} |
|
.shape-item-text { |
|
font-size: 12px; |
|
text-align: center; |
|
} |
|
.shape-type { |
|
display: -webkit-box; |
|
overflow-x: scroll; |
|
scrollbar-width: none; |
|
-ms-overflow-style: none; |
|
padding-bottom: 16px; |
|
} |
|
.shape-type-item { |
|
background: #F0F2F5; |
|
font-size: 12px; |
|
height: 7vw; |
|
padding: 0px 16px; |
|
border-radius: 4px; |
|
margin-right: 8px; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
.shape-active { |
|
background: #15CF5F; |
|
color: #fff; |
|
} |
|
.shape-box-input { |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
margin-top: 10px; |
|
} |
|
.shape-box-input-text { |
|
width: 100%; |
|
height: 40px; |
|
border: 1px solid #ccc; |
|
border-radius: 4px; |
|
padding: 0 10px; |
|
} |
|
.shape-text { |
|
position: absolute; |
|
z-index: 2; |
|
font-family: chinese; |
|
overflow: hidden; |
|
text-shadow: 1px 0px 1px #000000a1; |
|
white-space: pre-wrap; |
|
} |
|
.shape-item-image-round { |
|
border-radius: 50%; |
|
} |
|
.van-address-edit { |
|
padding: 0!important; |
|
} |
|
</style> |