
15 changed files with 1093 additions and 196 deletions
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 630 KiB |
@ -0,0 +1,170 @@ |
|||
<template> |
|||
<div class="custom-select" :class="{ 'is-open': isOpen }"> |
|||
<!-- 触发按钮 --> |
|||
<div class="select-trigger" @click="toggleDropdown"> |
|||
<slot name="trigger"> |
|||
{{ selectedItem ? selectedItem.label : placeholder }} |
|||
</slot> |
|||
<span class="arrow-icon">{{ isOpen ? "▲" : "▼" }}</span> |
|||
</div> |
|||
|
|||
<!-- 自定义内容 --> |
|||
<!-- 下拉内容 --> |
|||
<transition name="slide-fade"> |
|||
<div v-if="isOpen" class="select-dropdown"> |
|||
<slot v-if="isOpen" name="normal"></slot> |
|||
<div v-for="(item, index) in options" :key="index" class="dropdown-item" |
|||
:class="{ 'is-selected': isSelected(item) }" @click="selectItem(item)"> |
|||
<slot name="item" :item="item"> |
|||
{{ item.label }} |
|||
</slot> |
|||
</div> |
|||
<!-- <slot v-if="isOpen" name="options_null"></slot> --> |
|||
<div class="flex-between dropdown-item" v-if="options_null" @click="selectNullItem"> |
|||
<div class="left"> |
|||
<p class="one">暂无收款账号</p> |
|||
<p>暂时没有收款账号,我想稍后配置</p> |
|||
</div> |
|||
<div class="right"> |
|||
<img src="../assets/register/drop-selected.svg" alt=""> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</transition> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
options: { |
|||
type: Array, |
|||
required: true, |
|||
default: () => [], |
|||
}, |
|||
options_null: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: "请选择", |
|||
}, |
|||
value: { |
|||
type: [String, Number, Object], |
|||
default: null, |
|||
}, |
|||
|
|||
}, |
|||
data() { |
|||
return { |
|||
isOpen: false, |
|||
selectedItem: null, |
|||
}; |
|||
}, |
|||
watch: { |
|||
value: { |
|||
immediate: true, |
|||
handler(newVal) { |
|||
this.selectedItem = this.options.find((item) => item.value === newVal); |
|||
}, |
|||
}, |
|||
}, |
|||
methods: { |
|||
toggleDropdown() { |
|||
this.isOpen = !this.isOpen; |
|||
}, |
|||
selectItem(item) { |
|||
this.selectedItem = item; |
|||
this.$emit("input", item.value); // 更新 v-model 的值 |
|||
this.$emit("change", item); // 触发 change 事件 |
|||
this.isOpen = false; |
|||
}, |
|||
isSelected(item) { |
|||
return this.selectedItem && this.selectedItem.value === item.value; |
|||
}, |
|||
selectNullItem(){ |
|||
// this.$emit("input", null); // 更新 v-model 的值 |
|||
this.$emit("changeNormal", ''); // 触发 change 事件 |
|||
this.isOpen = false; |
|||
} |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.custom-select { |
|||
height: 38px; |
|||
position: relative; |
|||
font-family: Arial, sans-serif; |
|||
} |
|||
|
|||
.select-trigger { |
|||
border-radius: 2px; |
|||
opacity: 1; |
|||
background: #FFFFFF; |
|||
border: 1px solid #DFE2E6; |
|||
width: calc(100% - 24px); |
|||
padding: 10px 12px; |
|||
cursor: pointer; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.is-open .select-trigger { |
|||
border: 1px solid #006AFF; |
|||
transition: all .5s; |
|||
} |
|||
|
|||
.select-trigger:hover { |
|||
border-color: #006AFF; |
|||
transition: all .5s; |
|||
} |
|||
|
|||
.arrow-icon { |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.select-dropdown { |
|||
position: absolute; |
|||
top: 100%; |
|||
left: 0; |
|||
width: calc(100% - 24px); |
|||
border: 1px solid #ccc; |
|||
border-radius: 4px; |
|||
background-color: #fff; |
|||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
|||
z-index: 1000; |
|||
margin-top: 4px; |
|||
max-height: 384px; |
|||
overflow-y: auto; |
|||
padding: 0 12px 12px 12px; |
|||
} |
|||
|
|||
.dropdown-item { |
|||
padding: 12px 10px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.dropdown-item:hover { |
|||
background: #F6F7FA; |
|||
} |
|||
|
|||
.dropdown-item.is-selected { |
|||
background-color: transparent; |
|||
color: #fff; |
|||
} |
|||
|
|||
/* 展开收起动画 */ |
|||
.slide-fade-enter-active, |
|||
.slide-fade-leave-active { |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.slide-fade-enter-from, |
|||
.slide-fade-leave-to { |
|||
opacity: 0; |
|||
transform: translateY(-10px); |
|||
} |
|||
</style> |
@ -0,0 +1,90 @@ |
|||
// src/utils/request.js
|
|||
import axios from "axios"; |
|||
|
|||
// 创建 axios 实例
|
|||
const service = axios.create({ |
|||
baseURL: process.env.VUE_APP_BASE_API, // 从环境变量中读取 API 基础地址
|
|||
timeout: 10000, // 请求超时时间
|
|||
}); |
|||
|
|||
// 请求拦截器
|
|||
service.interceptors.request.use( |
|||
(config) => { |
|||
// 在发送请求之前做一些处理,例如添加 token
|
|||
const token = localStorage.getItem("token"); |
|||
if (token) { |
|||
config.headers["Authorization"] = `Bearer ${token}`; |
|||
} |
|||
return config; |
|||
}, |
|||
(error) => { |
|||
// 对请求错误做些什么
|
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
// 响应拦截器
|
|||
service.interceptors.response.use( |
|||
(response) => { |
|||
// 对响应数据做一些处理
|
|||
const res = response.data; |
|||
if (res.code !== 200) { |
|||
// 如果返回的 code 不是 200,则视为错误
|
|||
return Promise.reject(new Error(res.message || "请求失败")); |
|||
} |
|||
return res; |
|||
}, |
|||
(error) => { |
|||
// 对响应错误做些什么
|
|||
if (error.response) { |
|||
switch (error.response.status) { |
|||
case 401: |
|||
// 未授权,跳转到登录页
|
|||
window.location.href = "/login"; |
|||
break; |
|||
case 404: |
|||
// 资源未找到
|
|||
console.error("资源未找到"); |
|||
break; |
|||
case 500: |
|||
// 服务器错误
|
|||
console.error("服务器错误"); |
|||
break; |
|||
default: |
|||
console.error("请求失败", error.message); |
|||
} |
|||
} |
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
/** |
|||
* 封装请求方法 |
|||
* @param {string} method 请求方法 (GET, POST, PUT, DELETE 等) |
|||
* @param {string} url 请求地址 |
|||
* @param {object} data 请求参数 |
|||
* @param {object} config 其他 axios 配置 |
|||
* @returns {Promise} 返回请求结果 |
|||
*/ |
|||
const request = (method, url, data = {}, config = {}) => { |
|||
const lowerCaseMethod = method.toLowerCase(); |
|||
if (lowerCaseMethod === "get") { |
|||
// GET 请求将参数拼接到 URL 上
|
|||
return service({ |
|||
method: "get", |
|||
url, |
|||
params: data, |
|||
...config, |
|||
}); |
|||
} else { |
|||
// 其他请求(POST, PUT, DELETE 等)将参数放在请求体中
|
|||
return service({ |
|||
method: lowerCaseMethod, |
|||
url, |
|||
data, |
|||
...config, |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
export default request; |
@ -0,0 +1,62 @@ |
|||
<template> |
|||
<div class="domain-wrap"> |
|||
<p>设置自己的域名,需要做 2 步站外操作。如果暂时未准备好,或打算稍后设置,可以先选用平台免费域名,平台随时支持域名修改。 </p> |
|||
<div class="domain-box"> |
|||
<div class="domain-item"> |
|||
<p>1. 域名必须在阿里云完成 ICP 备案</p> |
|||
<p>备案网址:https://beian.aliyun.com</p> |
|||
</div> |
|||
<div class="domain-item"> |
|||
<p>2. 域名要解析到平台服务器上</p> |
|||
<p>备案网址:https://beian.aliyun.com</p> |
|||
</div> |
|||
<div class="domain-item"> |
|||
<p>3. 域名填写</p> |
|||
<el-input placeholder="填写完整域名" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
// import GuipInput from '@/components/GuipInput.vue'; |
|||
|
|||
export default { |
|||
name: '', |
|||
props: [''], |
|||
components: { |
|||
// GuipInput, |
|||
|
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
onSwitchChange(data) { |
|||
console.log(data, '---'); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style lang="scss"> |
|||
.domain-wrap { |
|||
.domain-item{ |
|||
margin-bottom: 10px; |
|||
} |
|||
.domain-item p:last-child{ |
|||
padding-left: 23px; |
|||
color: #8A9099; |
|||
} |
|||
p{ |
|||
text-align: left; |
|||
line-height: 18px; |
|||
margin-bottom: 8px; |
|||
} |
|||
.domain-box { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1 @@ |
|||
<!-- 站点列表 --> |
@ -0,0 +1,49 @@ |
|||
<template> |
|||
<div class="site-setting-wrap"> |
|||
|
|||
</div> |
|||
</template> |
|||
<script> |
|||
// import GuipInput from '@/components/GuipInput.vue'; |
|||
|
|||
export default { |
|||
// 站点设置 |
|||
name: '', |
|||
props: [''], |
|||
components: { |
|||
// GuipInput, |
|||
|
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
onSwitchChange(data) { |
|||
console.log(data, '---'); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style lang="scss"> |
|||
.domain-wrap { |
|||
.domain-item{ |
|||
margin-bottom: 10px; |
|||
} |
|||
.domain-item p:last-child{ |
|||
padding-left: 23px; |
|||
color: #8A9099; |
|||
} |
|||
p{ |
|||
text-align: left; |
|||
line-height: 18px; |
|||
margin-bottom: 8px; |
|||
} |
|||
.domain-box { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
} |
|||
} |
|||
</style> |
Loading…
Reference in new issue