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> |