forked from natuka/web.puabadge.com
1833 lines
55 KiB
Vue
1833 lines
55 KiB
Vue
<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" /> {{ toValueWithout("效果预览") }}</span>
|
||
</div>
|
||
<div class="step-container">
|
||
<div class="step-item">
|
||
<div class="step-content">
|
||
<div class="step-title">{{ toValueWithout("正面照片") }}</div>
|
||
<div class="step-desc">{{ toValueWithout("清晰的正面照片") }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="step-item active">
|
||
<div class="step-num">2</div>
|
||
<div class="step-content">
|
||
<div class="step-title">{{ toValueWithout("确认下单") }}</div>
|
||
<div class="step-desc">{{ toValueWithout("选择一个你喜欢的效果图下单") }}</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">{{ toValueWithout("总计大约需要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="" style="max-height: 320px;">
|
||
<!-- <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 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 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">
|
||
{{ toValueWithout("设计中") }}
|
||
</div>
|
||
<div v-else-if="item.status == 2" class="image-list-item-loading">
|
||
{{ toValueWithout("设计失败") }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="info-section">
|
||
<div class="info-item">
|
||
<div class="info-title" v-if="typeId == 1">{{ 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 == 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>
|
||
<div class="info-item">
|
||
<div class="info-title">{{ toValueWithout("3D全彩打印") }}</div>
|
||
<div class="info-content">{{ toValueWithout("工艺") }}</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 && typeId != 4">
|
||
<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">{{ toValueWithout(item.name) }}</div>
|
||
</div>
|
||
</block>
|
||
</div>
|
||
<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" />
|
||
</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">
|
||
下单后预计需要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">{{ toValueWithout("订购数量") }}</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">({{ 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>
|
||
</div>
|
||
<div class="address-box" v-if="orderStat.use_type == 2 && imageUrl && code == 'ACAYOAAC3PFCPO3CD6DVM1'">
|
||
<div class="order-title" style="padding: 0 16px;">{{ toValueWithout("地址识别") }}</div>
|
||
<div class="address-item">
|
||
<van-field
|
||
v-model="addressInput"
|
||
label=""
|
||
:placeholder="toValueWithout('请输入姓名、手机号、详细地址(如:张三 13800138000 广东省深圳市南山区科技园1号)')"
|
||
clearable
|
||
right-icon="search"
|
||
>
|
||
<template #button>
|
||
<van-button size="small" type="primary" @click="handleAddressInput" style="margin-left: 8px;">{{ toValueWithout("自动识别") }}</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;">{{ toValueWithout("识别结果") }}</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;">{{ toValueWithout("收货地址") }}</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="[toValueWithout('请选择'), toValueWithout('请选择'), toValueWithout('请选择')]"
|
||
@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=""> {{ toValueWithout("再次生成") }}</button>
|
||
<button @click="compare" :disabled="flag < 1" class="action-btn"><img class="action-img" src="@/assets/badge/duibi.png" alt=""> {{ toValueWithout("前后对比") }}</button>
|
||
<button @click="save" :disabled="flag < 1" class="action-btn"><img class="action-img" src="@/assets/badge/down.png" alt=""> {{ toValueWithout("保存图片") }}</button>
|
||
</div>
|
||
<div class="btn-box" v-if="trialCode == false">
|
||
<button @click="confirm" :disabled="flag < 1" class="confirm-btn">{{ toValueWithout("确认选择") }}</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<van-action-sheet v-model:show="showCompare" :title="toValueWithout('前后对比')" :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="toValueWithout('原图')" 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="toValueWithout('效果图')" style="width: 120px; height: 120px; border-radius: 8px;">
|
||
</div>
|
||
</div>
|
||
<div style="text-align: center; margin-top: 16px; color: #999; font-size: 12px;">
|
||
{{ toValueWithout("温馨提示:AI设计的徽章效果图是根据用户提供的照片机器学习生成的,多试几次就能找到你满意的徽章~") }}
|
||
</div>
|
||
</div>
|
||
</van-action-sheet>
|
||
|
||
<div v-if="showPreview">
|
||
<div class="preview-mask" @click="showPreview = false">
|
||
<img :src="imageWater" :alt="toValueWithout('徽章预览')" />
|
||
<p>{{ toValueWithout("长按图片 → 保存到相册") }}</p>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import { onMounted, ref, onUnmounted, nextTick } 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'
|
||
import { toValueWithout } from '@/lang/utils'
|
||
import { createTextPosition } from '@/utils/textHelper'
|
||
|
||
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 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')
|
||
|
||
function compare() {
|
||
showCompare.value = true
|
||
}
|
||
|
||
function sureReload() {
|
||
console.log('orderStat', orderStat.value)
|
||
if (orderStat.value.remain_count <= 0) {
|
||
showToast(toValueWithout('当前次数已用完,请重新购买'))
|
||
return
|
||
}
|
||
isPreview.value = false
|
||
showConfirmDialog({
|
||
title: toValueWithout('再次生成'),
|
||
message:
|
||
toValueWithout('请确认是否重新生成。'),
|
||
})
|
||
.then(() => {
|
||
group.value = newGroup.value
|
||
const params = {
|
||
pid: pid.value,
|
||
group: 1,
|
||
prod_id: prodId.value,
|
||
type_id: typeId.value,
|
||
kind_id: kindId.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.image_url
|
||
}
|
||
|
||
const createLog = () => {
|
||
badgeApi.createLog({
|
||
pid: pid.value,
|
||
group: 1,
|
||
prod_id: prodId.value,
|
||
type_id: typeId.value,
|
||
kind_id: kindId.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: 1,
|
||
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
|
||
if (typeId.value != 4) {
|
||
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(toValueWithout('请先选择下单数量'))
|
||
return
|
||
}
|
||
if (orderStat.value.use_type == 2 && !contact_name.value) {
|
||
showToast(toValueWithout('请先填写收货人信息'))
|
||
return
|
||
}
|
||
if (orderStat.value.use_type == 2 && !contact_mobile.value) {
|
||
showToast(toValueWithout('请先填写收货人手机号'))
|
||
return
|
||
}
|
||
if (orderStat.value.use_type == 2 && !province_id.value) {
|
||
showToast(toValueWithout('请先选择地区'))
|
||
return
|
||
}
|
||
if (orderStat.value.use_type == 2 && !address.value) {
|
||
showToast(toValueWithout('请先填写详细地址'))
|
||
return
|
||
}
|
||
getPosition()
|
||
loading.value = true
|
||
showConfirmDialog({
|
||
title: toValueWithout('确认下单'),
|
||
message:
|
||
toValueWithout('请再次确认是否选择这个模型下单,下单后预计需要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 ? 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) {
|
||
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: toValueWithout('下单成功'),
|
||
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 ? shapeText.value : frontSaveText.value,
|
||
back_text: backSaveText.value ? backSaveText.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) => {
|
||
console.log('getPosition', res)
|
||
}).catch((err) => {
|
||
console.log('getPosition', 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 = toValueWithout('照片数据分析中')
|
||
} else if (progress.value < 50) {
|
||
progressText.value = toValueWithout('提炼图像关键词')
|
||
} else if (progress.value < 75) {
|
||
progressText.value = toValueWithout('图像生成中')
|
||
} else {
|
||
progressText.value = toValueWithout('图像后处理与优化')
|
||
}
|
||
}
|
||
}, 300)
|
||
}
|
||
}
|
||
|
||
const newGroup = ref(0)
|
||
const isPreview = ref(false)
|
||
const getImageList = () => {
|
||
badgeApi.getImageList({
|
||
pid: pid.value,
|
||
group: 1,
|
||
prod_id: prodId.value
|
||
}).then((res: any) => {
|
||
console.log('getImageList', res)
|
||
const data = res || []
|
||
flag.value = data.flag
|
||
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.image_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.image_url
|
||
isPreview.value = true
|
||
} else {
|
||
originUrl.value = currentImage.origin_url
|
||
imageUrl.value = currentImage.origin_url
|
||
frameUrl.value = currentImage.frame_url
|
||
imageWater.value = currentImage.image_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].image_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].image_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 shapeIds = ref([])
|
||
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: typeId.value
|
||
}).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 && typeId.value == 2) {
|
||
shapeText.value = ''
|
||
topText.value = ''
|
||
bottomText.value = ''
|
||
topTextStyle.value = ''
|
||
bottomTextStyle.value = ''
|
||
shapeTextStyle.value = ''
|
||
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) => {
|
||
console.log('getShapeList', 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 topTextStyle = ref('')
|
||
const bottomTextStyle = ref('')
|
||
const textShow = (item: any, type: string = 'shape') => {
|
||
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);
|
||
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};`
|
||
} 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 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-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]/g, '');
|
||
return;
|
||
}
|
||
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 regex = /^[a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3\u00c0-\u017f\s~!@#¥%……&*()——+_)(*&^%$#@!~?><:"}{|、】【';、。,'.]+$/;
|
||
return regex.test(input);
|
||
}
|
||
|
||
const handleInput = (value: string, type: string = 'shape') => {
|
||
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;
|
||
}
|
||
}
|
||
if (typeId.value == 2) {
|
||
if (shapeId.value == 27 || shapeId.value == 28) {
|
||
type == 'top' ? topText.value = validValue : bottomText.value = validValue;
|
||
} else {
|
||
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;
|
||
}
|
||
if (typeId.value == 2) {
|
||
if (shapeId.value == 27 || shapeId.value == 28) {
|
||
type == 'top' ? topText.value = value : bottomText.value = value;
|
||
} else {
|
||
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) => {
|
||
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 && typeId.value == 2) {
|
||
shapeText.value = ''
|
||
topText.value = ''
|
||
bottomText.value = ''
|
||
topTextStyle.value = ''
|
||
bottomTextStyle.value = ''
|
||
shapeTextStyle.value = ''
|
||
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 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
|
||
// prodId.value = res.prod_id
|
||
badgeTypeId.value = res.type_id // 设置 badgeTypeId
|
||
getSizeList()
|
||
})
|
||
}
|
||
|
||
function handleBeforeUnload(_event: BeforeUnloadEvent) {
|
||
// code 的清除已在 main.ts 中统一处理,这里只处理页面特定的清理逻辑
|
||
// 清除轮询和进度条计时器
|
||
clearInterval(timer.value)
|
||
clearInterval(progressTimer.value)
|
||
// trialCode 和 userId 的清除由 main.ts 统一处理
|
||
}
|
||
|
||
const pid = ref(0)
|
||
const route = useRoute()
|
||
const prodId = ref(0)
|
||
const kindId = 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
|
||
}
|
||
if (route.query.kind_id) {
|
||
kindId.value = Number(route.query.kind_id)
|
||
}
|
||
getTrialCode()
|
||
|
||
// pid.value = 281505;
|
||
// group.value = 1;
|
||
// prodId.value = 7;
|
||
// getTypesList()
|
||
getImageList()
|
||
progressList()
|
||
setTimeout(() => {
|
||
getCompareImage()
|
||
}, 1000)
|
||
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;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
.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: flex-start;
|
||
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;
|
||
top: 14vw;
|
||
}
|
||
.shape-item-text {
|
||
font-size: 12px;
|
||
text-align: center;
|
||
max-width: 20vw;
|
||
}
|
||
.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-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 10px;
|
||
gap: 10px;
|
||
flex-direction: column;
|
||
}
|
||
.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;
|
||
}
|
||
.font-chinese {
|
||
font-family: chinese;
|
||
}
|
||
.shape-item-image-round {
|
||
border-radius: 50%;
|
||
}
|
||
.van-address-edit {
|
||
padding: 0!important;
|
||
}
|
||
</style> |