orgManager/src/views/operations/gameManage/platform/components/AddDialog.vue

581 lines
24 KiB
Vue
Raw Normal View History

2025-09-04 09:20:44 +08:00
<template>
<!-- 新增 -->
<el-dialog :title="addEditStatus=='add' ? t('新增') : t('修改')" align-center :close-on-click-modal="false" v-model="showDialog" :width="formAll.promotionalStyle==1 ?'1900px':'700px'" append-to-body>
<!-- <el-scrollbar max-height="900px" > -->
<el-form ref="formRef" :model="formAll" :rules="rules" label-width="130px" class="add-form">
<el-row :gutter="20">
<el-col :span="formAll.promotionalStyle==1?12:24">
<el-form-item :label="t('版式风格')" prop="iconType">
<el-radio-group v-model="formAll.iconType" @change="generateImage" style="margin-bottom: 0px">
<el-radio-button :value="1">{{ t('方形图标') }}</el-radio-button>
<el-radio-button :value="2">{{ t('竖版图标') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('平台名称')" prop="platformName">
<el-input v-model="formAll.platformName" disabled />
</el-form-item>
<el-form-item :label="t('最低准入')" prop="minAmount">
<number-input v-model="formAll.minAmount" ></number-input>
</el-form-item>
<el-form-item :label="t('平台宣传语')" prop="promotionalText">
<el-input v-model="formAll.promotionalText" :placeholder="t('请输入平台宣传语')" />
</el-form-item>
<el-form-item :label="t('平台跳转方式')" prop="iosJump">
<div style="width: 100%;display: flex;">
<div style="width: 80px;text-align: right;margin-right: 10px;">IOS:</div>
<el-radio-group v-model="formAll.iosJump" style="margin-bottom: 0px">
<el-radio :value="1">{{ t('内嵌') }}</el-radio>
<el-radio :value="2">{{ t('外链') }}</el-radio>
</el-radio-group>
</div>
<div style="width: 100%;display: flex;">
<div style="width: 80px;text-align: right;margin-right: 10px;">Android:</div>
<el-radio-group v-model="formAll.androidJump" style="margin-bottom: 0px">
<el-radio :value="1">{{ t('内嵌') }}</el-radio>
<el-radio :value="2">{{ t('外链') }}</el-radio>
</el-radio-group>
</div>
<div style="width: 100%;display: flex;">
<div style="width: 80px;text-align: right;margin-right: 10px;">H5:</div>
<el-radio-group v-model="formAll.h5Jump" style="margin-bottom: 0px">
<el-radio :value="1">{{ t('内嵌') }}</el-radio>
<el-radio :value="2">{{ t('外链') }}</el-radio>
</el-radio-group>
</div>
</el-form-item>
<el-form-item :label="t('宣传图样式')" prop="promotionalStyle">
<el-radio-group v-model="formAll.promotionalStyle">
<el-radio :value="1">{{ t('默认') }}</el-radio>
<el-radio :value="2">{{ t('自定义') }}</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="formAll.promotionalStyle == 1">
<el-form-item :label="t('宣传图图标')" prop="promotionalIcon">
<banner-icon @clickSelect="handleSelection('three',$event)" :disabled="shouwLoding" :imageUrl="formAll.promotionalIcon" :query="materialIcon" url="/game/material/group/select"></banner-icon>
</el-form-item>
</div>
<div v-if="formAll.promotionalStyle == 2">
<el-form-item :label="t('宣传图')" prop="customPromotionalImage">
<div v-if="formAll.iconType == 2" class="upload-box-vioce11">
<image-upload v-model="formAll.customPromotionalImage" :typeUpImg="3" :limit="1" :isShowTip="false"
:isdrag="true"></image-upload>
</div>
<div v-if="formAll.iconType == 1" class="upload-box-vioce22">
<image-upload v-model="formAll.customPromotionalImage" :typeUpImg="3" :limit="1" :isShowTip="false"
:isdrag="true"></image-upload>
</div>
<div >{{ t('只能上传png文件且不超过1MB图片尺寸为300px * 400px') }}</div>
</el-form-item>
<el-form-item :label="t('热门图')" prop="customPopularStyle">
<div v-if="formAll.iconType == 2" class="upload-box-vioce11">
<image-upload v-model="formAll.customPopularStyle" :typeUpImg="3" :limit="1" :isShowTip="false"
:isdrag="true"></image-upload>
</div>
<div v-if="formAll.iconType == 1" class="upload-box-vioce22">
<image-upload v-model="formAll.customPopularStyle" :typeUpImg="3" :limit="1" :isShowTip="false"
:isdrag="true"></image-upload>
</div>
<div >{{ t('只能上传png文件且不超过1MB图片尺寸为300px * 400px') }}</div>
</el-form-item>
</div>
</el-col>
<el-col :span="12" v-if="formAll.promotionalStyle == 1">
<div v-if="formAll.promotionalStyle == 1">
<el-form-item :label="t('图片背景')" prop="promotionalBackground">
<BannerColour @clickSelect="handleSelection('two',$event)" :disabled="shouwLoding" :backgroundColor="formAll.promotionalBackground" :query="materialTypess" url="/game/material/group/select"></BannerColour>
</el-form-item>
<el-form-item v-if="formAll.promotionalStyle == 1" :label="t('品牌LOGO')" prop="platformLogo">
<div class="upload-box-vioce" style="border:1px solid #409eff;border-radius: 5px;width: 213px;height:90px !important;text-align: center;display: flex;justify-content: center;align-items: center;">
<img style="width: 100px;" :src="fileHost+formAll.platformLogo"/>
</div>
</el-form-item>
<el-form-item v-if="formAll.promotionalStyle == 2" :label="t('品牌LOGO')" prop="customPlatformLogo">
<div class="upload-box-vioce">
<image-upload v-model="formAll.customPlatformLogo" :typeUpImg="3" :limit="1" :isShowTip="false"
:isdrag="true"></image-upload>
</div>
</el-form-item>
<el-form-item :label="t('宣传图预览')">
<div style=" position: absolute; top: -9999px; left: -9999px; visibility: hidden; ">
<div class="" v-if="formAll.iconType == 1" ref="captureArea" :style="`display: flex;align-items: center;justify-content: space-between;width: 400px;height: 150px;background: url(${formAll.popularStyleGb}) no-repeat;background-size: 100% 100%;`">
<div class="" style="width: 40%;">
<p style="margin: 0;text-align: center;"><img style="width: 100px;height: auto;" :src="formAll.platformLogoGb"/></p>
<p style="margin: 0;text-align: center;font-size: 16px;font-weight: 600;"> </p>
</div>
<div style="width: 60%;">
<div style="width: 260px;height: 150px;">
<img style="width: 100%;height: 100%;" :src="formAll.promotionalImageGb"/>
</div>
</div>
</div>
<div v-if="formAll.iconType == 2" class="" ref="captureArea" :style="`display: flex;align-items: center;justify-content: space-between;width: 210px;height: 280px;background: url(${formAll.popularStyleGb}) no-repeat;background-size: 100% 100%;`">
<div :style="`width: 210px; height: 280px;display: flex;align-items: center;position: relative;background: url(${formAll.promotionalImageGb});
background-size: cover;
background-position: center;
background-repeat: no-repeat;`">
<div class="" :style="`position: absolute;bottom: 0;left: 0;width: 100%;`">
<div style="margin: 0;text-align: center;position: relative;top: 0px;"><img style="width: 58px;height: auto;" crossorigin="anonymous" :src="formAll.platformLogoGb"/></div>
<div style="margin: 0;text-align: center;font-size: 16px;font-weight: 600;color: #fff;height: 35px;"> </div>
</div>
</div>
</div>
</div>
<img :src="resolvedImageUrl" style="border-radius: 5px;" alt="Generated Image" />
</el-form-item>
<el-form-item :label="t('热门图预览')">
<div style="position: absolute; top: -8999px; left: -8999px; visibility: hidden;">
<div class="" v-if="formAll.iconType == 1" ref="captureAreaPop" :style="`display: flex;align-items: center;justify-content: space-between;width: 400px;height: 150px;background: url(${formAll.popularStyleGb}) no-repeat;background-size: 100% 100%;`">
<div class="" style="width: 40%;">
<p style="margin: 0;text-align: center;"><img style="width: 100px;height: auto;" :src="formAll.platformLogoGb"/></p>
<p style="margin: 0;text-align: center;font-size: 16px;font-weight: 600;"> </p>
</div>
<div style="width: 60%;">
<div style="width: 260px;height: 150px;">
<img style="width: 100%;height: 100%;" :src="formAll.promotionalImageGb"/>
</div>
</div>
</div>
<div v-if="formAll.iconType == 2" class="" ref="captureAreaPop" :style="`display: flex;align-items: center;justify-content: space-between;width: 210px;height: 280px;background: url(${formAll.popularStyleGb}) no-repeat;background-size: 100% 100%;`">
<div :style="`width: 210px; height: 245px;display: flex;align-items: center;margin-top:35px; position: relative;background: url(${formAll.promotionalImageGb});
background-size: cover;
background-position: center;
background-repeat: no-repeat;`">
<div class="" :style="`position: absolute;top: -40px;left: 0;width: 100%;`">
<div style="margin: 0;text-align: center;"><img style="width: 70px;height: auto;" crossorigin="anonymous" :src="formAll.platformLogoGb"/></div>
</div>
<div class="" :style="`position: absolute;height:80px;bottom: 0;left: 0;width: 100%;`">
<div style="margin: 0;text-align: center;font-size: 16px;font-weight: 600;color: #fff;margin-top: 45px;height: 35px;"> </div>
</div>
</div>
</div>
</div>
<img :src="resolvedPopularStyle" style="border-radius: 5px;" alt="Generated Image" />
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<!-- </el-scrollbar> -->
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm" :loading="loadingButton">{{ t('确 定') }}</el-button>
<el-button @click="closeDialog">{{ t(' ') }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { formatTime,finalTimestamp } from '@/utils/ruoyi'; // 时间格式化
import ImageUpload from "@/components/ImageUpload";
import NumberInput from "@/components/NumberInput";
import { updatePlatform } from "@/api/game/platform";
import BannerColour from "@/components/BannerColour"; // Banner图颜色
import BannerIcon from "@/components/BannerIcon"; // Banner图标
import { getToken } from '@/utils/auth'
import { nextTick, onMounted, ref } from "vue"; // 获取当前实例
import { getLocalStorage } from "@/utils/auth";
const fileHost = getLocalStorage('fileUrl') || ''; // 文件host
import html2canvas from 'html2canvas';
const uploadUrl = getLocalStorage('uploadUrl');
const uploadImgUrl = ref(uploadUrl + "/file/upload/localSysFile/3"); // 上传的图片服务器地址
const loadingButton = ref(false);
const oldForm = shallowRef({ });
const langList = getLocalStorage('langList')?.filter(v => v.langType == 1 && v.langStatus).map(item => {
return {
label: item.name,
value: item.id
};
}); // 语种选项
const { proxy } = getCurrentInstance() // 获取当前实例
const emits = defineEmits(['submit', 'update:show']) // 自定义事件
const props = defineProps({ // 父组件向子组件传值
data: {
type: Object, // 父组件传值
default: {}
},
show: {
type: Boolean, // 显示
default: false
},
addEditStatus:{ // 添加/修改
type: String,
default: 'add'
},
modifyDate: { //修改数据
type: Object,
default: {}
}
})
const materialIcon = ref({ //图标请求参数
materialTypes:'2'
});
const materialTypess = ref({ //颜色请求参数
materialTypes:'1'
});
const showDialog = computed({ //弹窗显示控制
get() {
return props.show
},
set(value) {
emits('update:show', value)
}
})
const activeNameLang = ref(0);//语言切换选中
const langOptionAll = ref([]); // 语言tabs切换的数据
const formAll = reactive({
iconType:1, // 平台类型 1:方形图标 2:竖版图标
})
const loadImageAsBase64 = async(url)=> {
const res = await fetch(uploadUrl+url, { mode: 'cors' })
const blob = await res.blob()
return await new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.readAsDataURL(blob)
})
}
// 修改时数据更新相关内容
nextTick(async () => {
if (props.addEditStatus != 'edit') return;
Object.assign(formAll, props.modifyDate)
formAll.id = props.modifyDate.id;
formAll.iconType = props.modifyDate.iconType|| 1;
formAll.platformName = props.modifyDate.platformName;
formAll.minAmount = props.modifyDate.minAmount;
if (props.modifyDate.promotionalStyle == 1){
formAll.promotionalImage = props.modifyDate.promotionalImage;
formAll.platformLogoGb = await loadImageAsBase64(props.modifyDate.platformLogo);
formAll.popularStyleGb = await loadImageAsBase64(props.modifyDate.promotionalBackground);
formAll.promotionalImageGb = await loadImageAsBase64(props.modifyDate.promotionalIcon);
formAll.popularStyle = props.modifyDate.popularStyle;
}
formAll.promotionalStyle = props.modifyDate.promotionalStyle;
setTimeout(() => {
let objForm = { ...formAll }
oldForm.value = JSON.stringify(objForm);
}, 500);
});
const captureArea = ref(null);
const captureAreaPop = ref(null);
// 加入图片加载检测
const waitForImagesLoaded = async (container) => {
const imgs = container.querySelectorAll('img');
await Promise.all([...imgs].map(img => {
return new Promise(resolve => {
if (img.complete) return resolve();
img.onload = img.onerror = () => resolve();
});
}));
};
const shouwLoding = ref(false);
const generateImage = async () => {
shouwLoding.value = true;
const el = captureArea.value;
const el2 = captureAreaPop.value;
if (!el || !el2) return;
el.style.visibility = 'visible';
el2.style.visibility = 'visible';
await nextTick();
await waitForImagesLoaded(el);
await waitForImagesLoaded(el2);
await new Promise(resolve => setTimeout(resolve, 100));
const canvas = await html2canvas(el, {
useCORS: true,
backgroundColor: '#fff'
});
formAll.promotionalImage = canvas.toDataURL('image/png');
await new Promise(resolve => setTimeout(resolve, 100)); // 控制节奏
const canvas2 = await html2canvas(el2, {
useCORS: true,
backgroundColor: '#fff'
});
formAll.popularStyle = canvas2.toDataURL('image/png');
shouwLoding.value = false;
el.style.visibility = 'hidden';
el2.style.visibility = 'hidden';
};
//表单验证规则
const rules = reactive({
promotionalStyle: [
{ required: true, message: proxy.t('请选择icon图样式'), trigger: 'change' },
],
minAmount: [
{ required: true, message: proxy.t('请输入最小投注金额'), trigger: 'change' },
]
})
// 关闭弹窗
const closeDialog = () => {
showDialog.value = false
}
const resolvedImageUrl = computed(() => {
const url = formAll.promotionalImage
if (!url) return ''
if (url.startsWith('data:image/')) {
return url // base64 图片
}
if (url.startsWith('http://') || url.startsWith('https://')) {
return url // 完整 URL不加 fileHost
}
return fileHost + url // 相对路径,加上 fileHost
})
const resolvedPopularStyle = computed(() => {
const url = formAll.popularStyle
if (!url) return ''
if (url.startsWith('data:image/')) {
return url // base64 图片
}
if (url.startsWith('http://') || url.startsWith('https://')) {
return url // 完整 URL不加 fileHost
}
return fileHost + url // 相对路径,加上 fileHost
})
const getColorFromUrl = (url) => {
// 匹配最后一个斜杠后的颜色字符串,不带扩展名
const match = url.match(/\/([^\/]+)\.png$/)
if (match && match[1]) {
return `#${match[1]}` // 加上 #
}
return null
}
const colorGb = ref('');
const isShow = ref(true); // 是否显示
// 处理选中排版、颜色、图标和图片上传后的数据更新
const handleSelection = async(type, en) => {
switch (type) {
case 'two':
isShow.value = false;
const color = getColorFromUrl(en.url);
colorGb.value = color;
if (formAll.promotionalBackground == en.url){
return
}
formAll.popularStyleGb = await loadImageAsBase64(en.url);
formAll.promotionalBackground = en.url;
nextTick(() => {
isShow.value = true;
generateImage();
});
break;
case 'three':
isShow.value = false;
if (formAll.promotionalIcon == en.url){
return
}
formAll.promotionalImageGb = await loadImageAsBase64(en.url);
formAll.promotionalIcon = en.url;
nextTick(() => {
isShow.value = true;
generateImage();
});
break;
default:
break;
}
};
function base64ToFile(base64, filename = 'image.png') {
const arr = base64.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}
const uploadBase64Image = async(base64) =>{
if (!/^data:image\/(png|jpeg|jpg|gif|bmp);base64,/.test(base64)) {
// 不是 Base64说明已经是 URL直接返回
return { url: base64 };
}
const file = base64ToFile(base64, 'upload.png')
const formData = new FormData()
formData.append('file', file)
const res = await fetch(uploadImgUrl.value, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + getToken(),
},
body: formData
})
return await res.json()
}
// 提交
const formRef = ref(null)
// 提交修改和新增数据表单
const submitForm = async() => {
// 验证表单
formRef.value.validate(async(valid) => {
if (valid) {
loadingButton.value = true;
let promotionalImages = await uploadBase64Image(formAll.promotionalImage)
let popularStyles = await uploadBase64Image(formAll.popularStyle)
// 初始化表单数据
let formData = {
...formAll,
promotionalImage:promotionalImages.url,
popularStyle:popularStyles.url,
};
delete formData.platformLogoGb;
delete formData.popularStyleGb;
delete formData.promotionalImageGb;
if (JSON.stringify(formAll) != oldForm.value) {
updatePlatform(formData).then(res => {
loadingButton.value = false;
proxy.$modal.msgSuccess(proxy.t('修改成功!'));
emits('submit');
closeDialog();
}).catch(error => {
loadingButton.value = false;
});
}else{
loadingButton.value = false;
closeDialog();
}
}
});
};
</script>
<style scope lang="scss">
.w100 {
width: 100%;
}
.upload-box-vioce{
height: 90px !important;
overflow: hidden;
.upload-tips {
color: #999;
font-size: 12px;
line-height: 1.5;
padding-left: 10px;
}
.component-upload-image .el-upload--picture-card {
width: 180px !important;
height: 90px !important;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 180px!important;
height: 90px!important;
}
:deep(.el-upload-list) {
width:180px!important;
height: 90px!important;
.el-upload,
.el-upload-list--picture-card .el-upload-list__item {
width: 180px!important;
height: 90px!important;
}
.el-upload-list__item {
margin: 0;
border: none;
width: 180px!important;
height: 90px!important;
}
}
}
.upload-box-vioce11{
height: 400px !important;
overflow: hidden;
.upload-tips {
color: #999;
font-size: 12px;
line-height: 1.5;
padding-left: 10px;
}
.component-upload-image .el-upload--picture-card {
width: 300px !important;
height: 400px !important;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 300px!important;
height: 400px!important;
}
:deep(.el-upload-list) {
width:300px!important;
height: 400px!important;
.el-upload,
.el-upload-list--picture-card .el-upload-list__item {
width: 300px!important;
height: 400px!important;
}
.el-upload-list__item {
margin: 0;
border: none;
width: 300px!important;
height: 400px!important;
}
}
}
.upload-box-vioce22{
height: 200px !important;
overflow: hidden;
.upload-tips {
color: #999;
font-size: 12px;
line-height: 1.5;
padding-left: 10px;
}
.component-upload-image .el-upload--picture-card {
width: 400px !important;
height: 200px !important;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 400px!important;
height: 200px!important;
}
:deep(.el-upload-list) {
width:400px!important;
height: 200px!important;
.el-upload,
.el-upload-list--picture-card .el-upload-list__item {
width: 400px!important;
height: 200px!important;
}
.el-upload-list__item {
margin: 0;
border: none;
width: 400px!important;
height: 200px!important;
}
}
}
.disable-click {
pointer-events: none;
}
</style>