After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 210 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 426 B |
@ -0,0 +1,207 @@ |
|||||
|
<template> |
||||
|
<div class="upload-wrapper"> |
||||
|
<!-- 预览图片 --> |
||||
|
<div class="image-list" v-if="previewList.length>0"> |
||||
|
<div class="image-box" |
||||
|
v-for="(item, index) in previewList" :key="index"> |
||||
|
<img :src="item.url" class="image-preview" /> |
||||
|
<!-- <i class="el-icon-close delete-icon" @click.stop="removeImage(index)" />--> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 上传按钮 --> |
||||
|
<el-upload |
||||
|
class="image-uploader" |
||||
|
action="" |
||||
|
:http-request="uploadHandler" |
||||
|
:show-file-list="false" |
||||
|
:before-upload="beforeUpload" |
||||
|
:accept="acceptMime" |
||||
|
> |
||||
|
<GuipButton class="upload-button" type="ignore" :btnstyle="btnStyle"> |
||||
|
<div class="bgImg"></div> |
||||
|
<span>选择图片</span> |
||||
|
</GuipButton> |
||||
|
</el-upload> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import GuipButton from "@/components/GuipButton.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: "ImgUpload", |
||||
|
components: {GuipButton}, |
||||
|
props: { |
||||
|
value: { |
||||
|
type: [Array, File, null], |
||||
|
default: () => [] |
||||
|
}, |
||||
|
defaultUrls: { |
||||
|
type: Array, |
||||
|
default: () => [] |
||||
|
}, |
||||
|
acceptTypes: { |
||||
|
type: Array, |
||||
|
default: () => ['jpg', 'png'] |
||||
|
}, |
||||
|
sizeLimit: { |
||||
|
type: Number, |
||||
|
default: 2 // MB |
||||
|
}, |
||||
|
btnStyle:{ |
||||
|
type: Object, |
||||
|
default() { |
||||
|
return { |
||||
|
width: '118px', |
||||
|
color: '#23242B', |
||||
|
border: '1px solid #BABDC2', |
||||
|
background: '#F2F3F5', |
||||
|
'border-radius': '4px' |
||||
|
}; |
||||
|
} |
||||
|
}, |
||||
|
maxCount: { |
||||
|
type: Number, |
||||
|
default: 1 |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
previewList: [], |
||||
|
initialized: false |
||||
|
}; |
||||
|
}, |
||||
|
watch: { |
||||
|
defaultUrls(newUrls) { |
||||
|
if (!this.initialized && Array.isArray(newUrls) && newUrls.length) { |
||||
|
this.previewList = newUrls |
||||
|
.filter(url => !!url) |
||||
|
.map(url => ({ url })); |
||||
|
this.initialized = true; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
// 根据后缀数组生成 MIME 类型字符串,用于 <el-upload accept=""> |
||||
|
acceptMime() { |
||||
|
const map = { |
||||
|
jpg: 'image/jpeg', |
||||
|
jpeg: 'image/jpeg', |
||||
|
png: 'image/png', |
||||
|
webp: 'image/webp', |
||||
|
gif: 'image/gif' |
||||
|
}; |
||||
|
return this.acceptTypes |
||||
|
.map(ext => map[ext.toLowerCase()]) |
||||
|
.filter(Boolean) |
||||
|
.join(','); |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
beforeUpload(file) { |
||||
|
if(this.maxCount === 1){ |
||||
|
this.previewList = [] |
||||
|
} |
||||
|
|
||||
|
if (this.previewList.length >= this.maxCount) { |
||||
|
this.$message.warning(`最多只能上传 ${this.maxCount} 张图片`); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const isImage = file.type.startsWith("image/"); |
||||
|
const isLt2M = file.size / 1024 / 1024 < 2; |
||||
|
if (!isImage) { |
||||
|
this.$message.error("只能上传图片格式!"); |
||||
|
return false; |
||||
|
} |
||||
|
if (!isLt2M) { |
||||
|
this.$message.error("图片大小不能超过 2MB!"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const allowedTypes = this.acceptMime.split(','); |
||||
|
if (!allowedTypes.includes(file.type)) { |
||||
|
this.$message.error(`只允许上传 ${this.acceptTypes.join('/').toUpperCase()} 格式图片!`); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const url = URL.createObjectURL(file); |
||||
|
|
||||
|
if(this.maxCount === 1){ |
||||
|
this.previewList = [{ file, url }]; |
||||
|
this.$emit('input', { file, url }); |
||||
|
} else { |
||||
|
this.previewList.push({ file, url }); |
||||
|
|
||||
|
const files = this.previewList |
||||
|
.filter(item => item.file) |
||||
|
.map(item => item.file); |
||||
|
this.$emit('input', files); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
}, |
||||
|
uploadHandler() { |
||||
|
// 留空,使用自定义上传逻辑,由父组件决定是否提交 file 到服务器 |
||||
|
}, |
||||
|
removeImage(index) { |
||||
|
this.previewList.splice(index, 1); |
||||
|
const files = this.previewList |
||||
|
.filter(item => item.file) |
||||
|
.map(item => item.file); |
||||
|
this.$emit('input', files); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.upload-wrapper { |
||||
|
} |
||||
|
|
||||
|
.image-list { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 8px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.image-box { |
||||
|
position: relative; |
||||
|
width: 100px; |
||||
|
height: 100px; |
||||
|
} |
||||
|
.image-preview { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
object-fit: cover; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
.delete-icon { |
||||
|
position: absolute; |
||||
|
top: -6px; |
||||
|
right: -6px; |
||||
|
font-size: 16px; |
||||
|
color: white; |
||||
|
background: #f56c6c; |
||||
|
border-radius: 50%; |
||||
|
padding: 2px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.bgImg{ |
||||
|
width: 20px; |
||||
|
height: 16px; |
||||
|
margin-right: 6px; |
||||
|
background-image: url(@/assets/site/uploadIcon.svg); |
||||
|
} |
||||
|
.upload-button:hover{ |
||||
|
border: 1px solid #006AFF!important; |
||||
|
} |
||||
|
.upload-button:hover .bgImg { |
||||
|
background-image: url(@/assets/site/uploadIcon_light.svg); |
||||
|
} |
||||
|
.upload-button:hover span { |
||||
|
color: #006AFF; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,359 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<el-form class="el-row demo-ruleForm" ref="formRef" v-if="domainOptions.length>0" :rules="rules" :model="data"> |
||||
|
<GuipFormItem column="column" class="combo-formItem w540" :label="label" :class="label ? '' : 'combo-formItem-nolabel'"> |
||||
|
<div slot="formDom" class="self-drop-wrap flex w540"> |
||||
|
<GuipInput prop="prefix" v-model="data.prefix" style="width: 60%;" placeholder="仅支持数字、字母" @blur="inputEnd"></GuipInput> |
||||
|
<!-- 只用作选中内容展示 --> |
||||
|
<div @click="toggleDrop" class="point flex appendDrop" style="width: 40%;">{{data.domain}}</div> |
||||
|
</div> |
||||
|
<!--触发 真实下拉操作 --> |
||||
|
<!-- valueKey="id" displayKey="name" 不添加时,默认取值 value、label--> |
||||
|
<CustomDropdown slot="formDom" ref="dropDomain" width="100%" v-model="data.domain" |
||||
|
:options="domainOptions" @change="changeSelect" placeholder="请选择"> |
||||
|
<template #normal> |
||||
|
<div class="flex flex-between noraml-jump"> |
||||
|
<div class="left"> |
||||
|
<b>添加新域名</b> |
||||
|
<p class="one ft12">域名需要在阿里云完成ICP备案并解析到平台服务器</p> |
||||
|
<p class="ft12">如果暂时未准备好,可先选用平台免费域名,随时支持域名修改。 </p> |
||||
|
</div> |
||||
|
<div class="right"> |
||||
|
<GuipButton size="form" @click="bindNewDomain">前往绑定</GuipButton> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
</CustomDropdown> |
||||
|
</GuipFormItem> |
||||
|
</el-form> |
||||
|
|
||||
|
<GuipDialog :dialogVisible="dialogVisible" :show-close-button="false" :showFooterButton="false" |
||||
|
type="center" title="温馨提示"> |
||||
|
<div class="domain-wrap"> |
||||
|
<el-form class="el-row demo-ruleForm" ref="formRef" :rules="formRules" :model="formData"> |
||||
|
<p class="p-info">设置自己的域名,需要做 2 步站外操作。如果暂时未准备好,或打算稍后设置,可以先选用平台免费域名,平台随时支持域名修改。 </p> |
||||
|
<p class="p-title"><span>1.</span>域名必须在阿里云完成 ICP 备案</p> |
||||
|
<p class="p-desc mb18">备案网址:https://beian.aliyun.com</p> |
||||
|
<p class="p-title"><span>2.</span>域名要解析到平台服务器上</p> |
||||
|
<p class="p-desc mb18">记录类型:CNAME;记录值:lunwen.kuailedns.com</p> |
||||
|
<p class="p-title"><span>3.</span>域名填写</p> |
||||
|
<GuipInput width="458px" placeholder="填写完整域名" class="mb18" prop="domain" v-model="formData.domain"></GuipInput> |
||||
|
<p class="p-title"><span>4.</span>域名备案号</p> |
||||
|
<GuipInput width="458px" placeholder="填写备案号" class="mb32" prop="beian" v-model="formData.beian"></GuipInput> |
||||
|
<div class="flex flex-between"> |
||||
|
<GuipButton type="ignore" :btnstyle="{'width': '247px', 'border-radius': '4px'}" @click="handleCancel">先用平台免费域名</GuipButton> |
||||
|
<GuipButton type="primary" :btnstyle="{'width': '247px', 'border-radius': '4px'}" :loading="show_step" @click="handleConfirm" >准备完毕,验证自有域名</GuipButton> |
||||
|
</div> |
||||
|
<div class="domain-step" v-if="show_step"> |
||||
|
<div class="flex mr12"> |
||||
|
<GuipButton v-if="step1 === 1" type="text" :loading="true" :btnstyle="{'color': '#1675FF', 'font-size': '12px'}"> |
||||
|
验证域名解析 |
||||
|
</GuipButton> |
||||
|
<GuipButton v-if="step1 === 2" type="text" :btnstyle="{'color': '#1675FF', 'font-size': '12px'}"> |
||||
|
<img src="@/assets/site/step_success.png" alt="" class="mr5"><span>域名已解析</span> |
||||
|
</GuipButton> |
||||
|
<GuipButton v-if="step1 === 3" type="text" :btnstyle="{'color': '#FF4D4F', 'font-size': '12px'}"> |
||||
|
<img src="@/assets/site/step_error.png" alt="" class="mr5"><span>域名未解析</span> |
||||
|
</GuipButton> |
||||
|
</div> |
||||
|
<div class="flex"> |
||||
|
<img src="@/assets/site/next-line.png" alt=""> |
||||
|
</div> |
||||
|
<div class="flex ml12"> |
||||
|
<GuipButton v-if="step2 === 0" type="text" :btnstyle="{'color': '#8A9099', 'font-size': '12px'}"> |
||||
|
<img src="@/assets/site/step_wait.png" alt="" class="mr5"><span>验证ICP备案</span> |
||||
|
</GuipButton> |
||||
|
<GuipButton v-if="step2 === 1" type="text" :loading="true" :btnstyle="{'color': '#1675FF', 'font-size': '12px'}"> |
||||
|
验证ICP备案 |
||||
|
</GuipButton> |
||||
|
<GuipButton v-if="step2 === 3" type="text" :btnstyle="{'color': '#FF4D4F', 'font-size': '12px'}"> |
||||
|
<img src="@/assets/site/step_error.png" alt="" class="mr5"><span>ICP未备案</span> |
||||
|
</GuipButton> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</GuipDialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script> |
||||
|
import GuipInput from "@/components/GuipInput.vue"; |
||||
|
import GuipDialog from "@/components/GuipDialog.vue"; |
||||
|
import GuipButton from "@/components/GuipButton.vue"; |
||||
|
import GuipFormItem from "@/components/GuipFormItem.vue"; |
||||
|
import CustomDropdown from "@/components/CustomDropdown.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: 'domainBind', |
||||
|
props:['label'], |
||||
|
components: { |
||||
|
CustomDropdown, GuipFormItem, |
||||
|
GuipButton, |
||||
|
GuipDialog, |
||||
|
GuipInput |
||||
|
}, |
||||
|
data(){ |
||||
|
return { |
||||
|
domainOptions:[], |
||||
|
prefix: '', |
||||
|
domain: '', |
||||
|
dialogVisible: false, |
||||
|
data: { |
||||
|
prefix: '', |
||||
|
domain: '', |
||||
|
}, |
||||
|
rules:{ |
||||
|
prefix: [ |
||||
|
{ required: true, message: '请输入域名前缀', trigger: 'blur' } |
||||
|
], |
||||
|
}, |
||||
|
formData: { |
||||
|
domain: '', |
||||
|
beian: '', |
||||
|
}, |
||||
|
formRules: { |
||||
|
domain: [ |
||||
|
{ required: true, message: '请输入域名', trigger: 'blur' }, |
||||
|
{ |
||||
|
pattern: /^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$/, |
||||
|
message: '格式错误,请输入正确域名', |
||||
|
trigger: 'blur' |
||||
|
} |
||||
|
], |
||||
|
beian: [ |
||||
|
{ required: true, message: '请输入域名备案号', trigger: 'blur' } |
||||
|
], |
||||
|
}, |
||||
|
show_step: false, |
||||
|
step1: 1, //1正在验证 2验证成功 3验证失败 |
||||
|
step2: 0 //0等待验证 1正在验证 3验证失败 |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
formData: { |
||||
|
deep: true, |
||||
|
handler() { |
||||
|
this.show_step = false; |
||||
|
this.step1 = 1 |
||||
|
this.step2 = 0 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
mounted(){ |
||||
|
this.getDomainList() |
||||
|
}, |
||||
|
methods:{ |
||||
|
getDomainList() { |
||||
|
const that = this |
||||
|
that.$http('POST', '/agentnew/ajax_get_private_domains', {}, { |
||||
|
headers: { |
||||
|
'Auth': this.token |
||||
|
} |
||||
|
}).then(response => { |
||||
|
if(response.status){ |
||||
|
this.data.domain = '.'+response.data[0] |
||||
|
that.domainOptions = response.data.map(item => ({ |
||||
|
label: '.'+item, |
||||
|
value: '.'+item |
||||
|
})); |
||||
|
return true |
||||
|
} |
||||
|
that.$message.error(response.info); |
||||
|
}).catch(error => { |
||||
|
console.error(error, 'error') |
||||
|
}) |
||||
|
}, |
||||
|
toggleDrop(e) { |
||||
|
this.$refs.dropDomain.toggleDropdown(e) |
||||
|
}, |
||||
|
inputEnd(){ |
||||
|
this.$emit('handleEvent', this.data) |
||||
|
}, |
||||
|
changeSelect() { |
||||
|
this.$emit('handleEvent', this.data) |
||||
|
}, |
||||
|
bindNewDomain(){ |
||||
|
this.dialogVisible = true |
||||
|
}, |
||||
|
handleCancel(){ |
||||
|
this.dialogVisible = false |
||||
|
}, |
||||
|
handleConfirm(){ |
||||
|
this.$refs.formRef.validate((valid) => { |
||||
|
if (valid) this.verfiyDNS(); |
||||
|
}); |
||||
|
}, |
||||
|
verfiyDNS() { |
||||
|
this.show_step = true |
||||
|
|
||||
|
const that = this |
||||
|
that.$http('POST', '/agentnew/ajax_check_dns', { |
||||
|
domain: that.formData.domain |
||||
|
}, { |
||||
|
headers: { |
||||
|
'Auth': this.token |
||||
|
} |
||||
|
}).then(response => { |
||||
|
if(response.status){ |
||||
|
that.step1 = 2 |
||||
|
that.step2 = 1 |
||||
|
that.verfiyBeian() |
||||
|
} else { |
||||
|
that.step1 = 3 |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error(error, 'error') |
||||
|
}) |
||||
|
}, |
||||
|
verfiyBeian(){ |
||||
|
const that = this |
||||
|
that.$http('POST', '/agentnew/ajax_check_beian', { |
||||
|
domain: that.formData.domain |
||||
|
},{ |
||||
|
headers:{ |
||||
|
'Auth': this.token |
||||
|
} |
||||
|
}).then(response => { |
||||
|
if(response.status){ |
||||
|
that.addDomain() |
||||
|
} else { |
||||
|
that.step2 = 3 |
||||
|
} |
||||
|
|
||||
|
}).catch(error => { |
||||
|
console.error(error, 'error') |
||||
|
}) |
||||
|
}, |
||||
|
addDomain(){ |
||||
|
const that = this |
||||
|
that.$http('POST', '/agentnew/ajax_add_domain', { |
||||
|
domain: that.formData.domain, |
||||
|
beian: that.formData.beian |
||||
|
},{ |
||||
|
headers:{ |
||||
|
'Auth': this.token |
||||
|
} |
||||
|
}).then(response => { |
||||
|
if(response.status){ |
||||
|
that.data.domain = that.formData.domain |
||||
|
that.domainOptions.unshift({ |
||||
|
label: '.'+that.formData.domain, |
||||
|
value: '.'+that.formData.domain |
||||
|
}) |
||||
|
that.show_step = false |
||||
|
that.dialogVisible = false |
||||
|
that.$message.success('添加成功'); |
||||
|
return true |
||||
|
} |
||||
|
that.$message.error(response.info); |
||||
|
}).catch(error => { |
||||
|
console.error(error, 'error') |
||||
|
}) |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<style scoped lang="scss"> |
||||
|
.w540 { |
||||
|
width: 540px!important; |
||||
|
} |
||||
|
.mb18{ |
||||
|
margin-bottom: 18px; |
||||
|
} |
||||
|
.mb32{ |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
.mr12{ |
||||
|
margin-right: 12px; |
||||
|
} |
||||
|
.ml12{ |
||||
|
margin-left: 12px; |
||||
|
} |
||||
|
.mr5{ |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
|
||||
|
.combo-formItem-nolabel{ |
||||
|
::v-deep { |
||||
|
.form-item-top{ |
||||
|
display: none; |
||||
|
height: 0; |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.combo-formItem { |
||||
|
letter-spacing: 0.08em; |
||||
|
::v-deep { |
||||
|
.form-item-bottom { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.select-trigger { |
||||
|
background: #F6F7FA; |
||||
|
border-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.is-open .select-trigger { |
||||
|
border-color: #006AFF; |
||||
|
} |
||||
|
|
||||
|
.el-input__inner { |
||||
|
border-radius: 2px 0 0 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.self-drop-wrap { |
||||
|
position: absolute; |
||||
|
z-index: 1; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.appendDrop { |
||||
|
height: 38px; |
||||
|
align-items: center; |
||||
|
border-radius: 0 2px 2px 0; |
||||
|
border: 1px solid #DFE2E6; |
||||
|
border-left-color: transparent; |
||||
|
justify-content: center; |
||||
|
box-sizing: border-box; |
||||
|
padding: 0 30px 0 12px; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
&:hover { |
||||
|
border: 1px solid #006AFF; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.domain-wrap{ |
||||
|
letter-spacing: 0.08em; |
||||
|
font-size: 14px; |
||||
|
.p-info{ |
||||
|
color: #626573; |
||||
|
margin-bottom: 24px; |
||||
|
} |
||||
|
.p-title{ |
||||
|
color: #23242B; |
||||
|
margin-bottom: 8px; |
||||
|
span{ |
||||
|
width: 23px; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
} |
||||
|
.p-desc{ |
||||
|
color: #8A9099; |
||||
|
padding-left: 23px; |
||||
|
} |
||||
|
|
||||
|
.domain-step{ |
||||
|
margin-top: 16px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
</style> |