支付优化
CI/CD / build (push) Successful in 4m59s
Details
CI/CD / build (push) Successful in 4m59s
Details
This commit is contained in:
parent
4551a1537d
commit
57edd9bbf9
|
|
@ -0,0 +1,37 @@
|
|||
# 实现步骤
|
||||
|
||||
1. **修改导航组件**:在 `AppSidebar.vue` 中添加备案号图标项
|
||||
- 在 `coreMenuItems` 数组末尾添加一个新的菜单项
|
||||
- 使用自定义 SVG 实现圆形叹号图标(或使用现有图标组件)
|
||||
|
||||
2. **实现悬浮弹窗功能**:
|
||||
- 添加一个响应式状态来控制弹窗的显示/隐藏
|
||||
- 实现鼠标悬停和点击事件处理
|
||||
- 设计弹窗样式,确保在右侧悬浮显示
|
||||
- 添加备案号文本:"蜀ICP备2024078618号-2"
|
||||
|
||||
3. **样式设计**:
|
||||
- 设计圆形图标样式,与现有导航项保持一致
|
||||
- 设计弹窗样式,包含背景、边框、阴影等
|
||||
- 确保弹窗在不同屏幕尺寸下正确显示
|
||||
|
||||
4. **响应式处理**:
|
||||
- 桌面端:鼠标悬停显示弹窗
|
||||
- 移动端:点击图标显示/隐藏弹窗
|
||||
- 添加媒体查询,确保在不同设备上的兼容性
|
||||
|
||||
5. **代码优化**:
|
||||
- 确保代码符合项目的代码规范
|
||||
- 添加适当的注释
|
||||
- 测试交互效果
|
||||
|
||||
# 预期效果
|
||||
|
||||
- 在导航菜单中添加一个圆形叹号图标
|
||||
- 鼠标悬停或点击图标时,在右侧显示包含备案号的悬浮弹窗
|
||||
- 弹窗具有良好的视觉效果和交互体验
|
||||
- 适配移动端、桌面端和平板端
|
||||
|
||||
# 文件修改
|
||||
|
||||
- `d:\work\Aiproject\DeotalandAi\apps\frontend\src\components\layout\AppSidebar.vue`
|
||||
|
|
@ -6,6 +6,7 @@ import AppHeader from '@/components/layout/AppHeader.vue'
|
|||
import AppSidebar from '@/components/layout/AppSidebar.vue'
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import footerCom from './components/footerBeiAn/index.vue';
|
||||
import {isWeChatBrowser} from '@deotaland/utils';
|
||||
const authStore = useAuthStore();
|
||||
authStore.updateUserInfo()
|
||||
const route = useRoute()
|
||||
|
|
@ -42,6 +43,9 @@ const closeMethods = {
|
|||
qmLoading.value = false;
|
||||
}
|
||||
}
|
||||
const isCn =()=>{
|
||||
return window.location.href.indexOf('cn')>-1
|
||||
}
|
||||
window.setElLoading = (qp=false)=>{
|
||||
if(qp){
|
||||
qmLoading.value = true
|
||||
|
|
@ -96,7 +100,7 @@ onMounted(() => {
|
|||
<div v-else>
|
||||
<router-view />
|
||||
</div>
|
||||
<footerCom />
|
||||
<footerCom v-if="isCn()" />
|
||||
</template>
|
||||
<style>
|
||||
*{
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ import StripePaymentForm from '@/components/StripePaymentForm.vue'
|
|||
import { Country, State } from 'country-state-city'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { PayServer } from '@deotaland/utils'
|
||||
import { requestUtils,clientApi } from '@deotaland/utils'
|
||||
import { requestUtils,clientApi,environmentUtils } from '@deotaland/utils'
|
||||
import { PurchaseModal as PurchaseModalClass } from './index.js'
|
||||
const payserver = new PayServer();
|
||||
const purchaseModal = new PurchaseModalClass();
|
||||
|
|
@ -249,9 +249,11 @@ const emit = defineEmits(['close'])
|
|||
const onClose = () => emit('close')
|
||||
const addons = ref({ matte:false, gloss:false, base:false })
|
||||
const qty = ref(1)
|
||||
const ipName = ref('')
|
||||
const ipName = ref('doll')
|
||||
const contact = ref({ emailOrPhone:'', subscribe:false })
|
||||
const shipping = ref({ country:'US', firstName:'', lastName:'', postalCode:'', state:'', city:'', address1:'', address2:'', phone:'', saveInfo:false })
|
||||
// 初始国家默认值,根据环境动态设置
|
||||
const initialCountry = ref('US')
|
||||
const shipping = ref({ country: initialCountry.value, firstName:'', lastName:'', postalCode:'', state:'', city:'', address1:'', address2:'', phone:'', saveInfo:false })
|
||||
const countryOptions = ref([])
|
||||
const stateOptions = ref([])
|
||||
const showStripe = ref(false)
|
||||
|
|
@ -284,7 +286,6 @@ const isPayButtonDisabled = computed(() => {
|
|||
shipping.value.city.trim() &&
|
||||
shipping.value.address1.trim() &&
|
||||
shipping.value.phone.trim() &&
|
||||
contact.value.emailOrPhone.trim() &&
|
||||
ipName.value.trim()
|
||||
)
|
||||
})
|
||||
|
|
@ -458,6 +459,17 @@ const updateCountryOptions = () => {
|
|||
|
||||
onMounted(() => {
|
||||
try {
|
||||
// 检查是否为国内环境
|
||||
const envResult = environmentUtils.quickDetectEnvironment()
|
||||
if (envResult.isDomestic) {
|
||||
initialCountry.value = 'CN'
|
||||
// 如果没有本地存储的地址信息,默认选择中国
|
||||
const savedShipping = localStorage.getItem('purchase_shipping')
|
||||
if (!savedShipping) {
|
||||
shipping.value.country = 'CN'
|
||||
}
|
||||
}
|
||||
|
||||
const s = localStorage.getItem('purchase_shipping')
|
||||
if (s) Object.assign(shipping.value, JSON.parse(s))
|
||||
const c = localStorage.getItem('purchase_contact')
|
||||
|
|
|
|||
|
|
@ -26,6 +26,32 @@
|
|||
</transition>
|
||||
</div>
|
||||
</li>
|
||||
<!-- 备案号图标 -->
|
||||
<li v-if="false">
|
||||
<div
|
||||
class="nav-item record-item"
|
||||
@mouseenter="showRecordPopup = true"
|
||||
@mouseleave="showRecordPopup = false"
|
||||
>
|
||||
<div class="nav-icon record-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path fill="currentColor" d="M512 896a384 384 0 1 0 0-768 384 384 0 0 0 0 768zm0 64a448 448 0 1 1 0-896 448 448 0 0 1 0 896z"/>
|
||||
<path fill="currentColor" d="M480 384v384a32 32 0 0 0 64 0V384a32 32 0 0 0-64 0zm0-128a32 32 0 0 0-32 32v32a32 32 0 0 0 64 0v-32a32 32 0 0 0-32-32z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 备案号悬浮弹窗 -->
|
||||
<div
|
||||
v-if="showRecordPopup"
|
||||
class="record-popup"
|
||||
@mouseenter="showRecordPopup = true"
|
||||
@mouseleave="showRecordPopup = false"
|
||||
>
|
||||
<div class="record-content">
|
||||
蜀ICP备2024078618号-2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
|
@ -107,6 +133,7 @@ const authStore = useAuthStore()
|
|||
// 响应式状态
|
||||
const isMobile = ref(window.innerWidth < 768)
|
||||
const remainingPoints = ref(1280)
|
||||
const showRecordPopup = ref(false)
|
||||
|
||||
// 计算属性
|
||||
const currentUser = computed(() => authStore.user)
|
||||
|
|
@ -212,7 +239,8 @@ onUnmounted(() => {
|
|||
border-right: 1px solid var(--border-color, #e5e7eb);
|
||||
/* transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-x: visible;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
|
@ -788,4 +816,143 @@ onUnmounted(() => {
|
|||
.sidebar-nav::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-secondary, #6b7280);
|
||||
}
|
||||
|
||||
/* 备案号图标样式 */
|
||||
.record-item {
|
||||
cursor: pointer;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
padding: 8px 0 !important;
|
||||
min-height: auto !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.record-item::before {
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.record-item:hover {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.record-icon svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.record-item:hover .record-icon svg {
|
||||
transform: scale(1.1);
|
||||
filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.5));
|
||||
}
|
||||
|
||||
/* 备案号悬浮弹窗样式 */
|
||||
.record-popup {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
margin-left: 16px;
|
||||
background: var(--sidebar-bg, #ffffff);
|
||||
border: 1px solid var(--border-color, #e5e7eb);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
box-shadow: 0 8px 32px rgba(107, 70, 193, 0.16),
|
||||
0 2px 8px rgba(107, 70, 193, 0.08);
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
white-space: nowrap;
|
||||
backdrop-filter: blur(12px);
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
.record-popup::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border: 8px solid transparent;
|
||||
border-right-color: var(--border-color, #e5e7eb);
|
||||
}
|
||||
|
||||
.record-popup::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border: 7px solid transparent;
|
||||
border-right-color: var(--sidebar-bg, #ffffff);
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.record-content {
|
||||
font-size: 14px;
|
||||
color: var(--text-primary, #1f2937);
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-50%) translateX(-8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端样式调整 */
|
||||
@media (max-width: 767px) {
|
||||
.record-popup {
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
transform: translateX(-50%);
|
||||
margin-left: 0;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.record-popup::before,
|
||||
.record-popup::after {
|
||||
right: 50%;
|
||||
top: 0;
|
||||
transform: translateY(-100%) translateX(50%);
|
||||
}
|
||||
|
||||
.record-popup::before {
|
||||
border: 8px solid transparent;
|
||||
border-bottom-color: var(--border-color, #e5e7eb);
|
||||
}
|
||||
|
||||
.record-popup::after {
|
||||
border: 7px solid transparent;
|
||||
border-bottom-color: var(--sidebar-bg, #ffffff);
|
||||
margin-top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 折叠状态下的弹窗位置调整 */
|
||||
.app-sidebar:not(.sidebar-mobile) .record-popup {
|
||||
left: calc(100% + 16px);
|
||||
}
|
||||
|
||||
/* 深色主题适配 */
|
||||
.dark .record-popup {
|
||||
background: var(--sidebar-bg, #1f2937);
|
||||
color: var(--text-primary, #f9fafb);
|
||||
}
|
||||
|
||||
.dark .record-popup::after {
|
||||
border-right-color: var(--sidebar-bg, #1f2937);
|
||||
border-bottom-color: var(--sidebar-bg, #1f2937);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
import { isWeChatBrowser } from '@deotaland/utils'
|
||||
let savedLang = localStorage.getItem('lang') || (isWeChatBrowser()?'zh':'en')
|
||||
const isZh = ()=>{
|
||||
if(isWeChatBrowser()){
|
||||
return true
|
||||
}
|
||||
if(window.location.href.indexOf('cn')>-1){
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
let savedLang = localStorage.getItem('lang') || (isZh()?'zh':'en')
|
||||
export default {
|
||||
legacy: false,
|
||||
locale: savedLang,
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ const goToPhoneLogin = () => {
|
|||
// 页面挂载时初始化认证状态
|
||||
onMounted(() => {
|
||||
// detectEnvironment()
|
||||
isWeChatBrowser()&&goToPhoneLogin()
|
||||
// isWeChatBrowser()&&goToPhoneLogin()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,30 +35,30 @@
|
|||
{{ t('nav.land') }}
|
||||
</a> -->
|
||||
|
||||
<a
|
||||
<!-- <a
|
||||
href="#"
|
||||
class="text-sm font-medium text-gray-300 hover:text-white transition-colors"
|
||||
>
|
||||
{{ t('nav.creator') }}
|
||||
</a>
|
||||
<a
|
||||
</a> -->
|
||||
<!-- <a
|
||||
href="#"
|
||||
class="text-sm font-medium text-gray-300 hover:text-white transition-colors"
|
||||
>
|
||||
{{ t('nav.done') }}
|
||||
</a>
|
||||
<a
|
||||
</a> -->
|
||||
<!-- <a
|
||||
href="#"
|
||||
class="text-sm font-medium text-gray-300 hover:text-white transition-colors"
|
||||
>
|
||||
{{ t('nav.about') }}
|
||||
</a>
|
||||
<router-link
|
||||
</a> -->
|
||||
<!-- <router-link
|
||||
to="/points-recharge"
|
||||
class="text-sm font-medium text-gray-300 hover:text-white transition-colors"
|
||||
>
|
||||
pricing
|
||||
</router-link>
|
||||
</router-link> -->
|
||||
</nav>
|
||||
|
||||
<!-- Right Action & Mobile Toggle -->
|
||||
|
|
@ -519,12 +519,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Company -->
|
||||
<div class="flex flex-col gap-4">
|
||||
<!-- <div class="flex flex-col gap-4">
|
||||
<h4 class="font-semibold text-gray-500 text-sm uppercase tracking-wider">{{ t('footer.company') }}</h4>
|
||||
<div class="flex flex-col gap-2">
|
||||
<a href="#" class="text-gray-300 hover:text-white text-sm">{{ t('footer.about') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Action -->
|
||||
<div class="flex flex-col gap-4">
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ export default defineConfig({
|
|||
// 配置代理解决CORS问题
|
||||
proxy: {
|
||||
'/api': {
|
||||
// target: 'https://api.deotaland.ai',
|
||||
target: 'http://api.deotaland.local',
|
||||
target: 'https://api.deotaland.ai',
|
||||
// target: 'http://api.deotaland.local',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,6 @@ export class PayServer {
|
|||
payReducerUrl = `${window.location.origin}/#/order-management`
|
||||
pamras = {
|
||||
product_id: orderInfo.product_id,
|
||||
shop_id: orderInfo.shop_id || '',
|
||||
"methods": [
|
||||
"card"
|
||||
],
|
||||
|
|
@ -167,6 +166,9 @@ export class PayServer {
|
|||
"order_info": orderInfo.order_info,
|
||||
"coupon_ids": orderInfo.coupon_ids || [],
|
||||
}
|
||||
if(orderInfo.shop_id){
|
||||
pamras.shop_id = orderInfo.shop_id
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
payReducerUrl = `${window.location.origin}/#/points-recharge`
|
||||
|
|
@ -182,6 +184,7 @@ export class PayServer {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let requestUrl = {
|
||||
1: clientApi.default.createPayorOrder,
|
||||
2: clientApi.default.CREATE_RECHARGE_ORDER,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const getEnvBaseURL = () => {
|
|||
const hostname = window.location.hostname;
|
||||
if(hostname=='localhost'){
|
||||
baseURL = '/api'
|
||||
}else if(hostname.indexOf('deotaland.ai')>-1){
|
||||
}else if(hostname.indexOf('deotaland.ai')>-1||hostname.indexOf('deota.cn')>-1){
|
||||
baseURL = 'https://api.deotaland.ai'
|
||||
}else if(hostname.indexOf('deotaland.local')>-1){
|
||||
baseURL = 'http://api.deotaland.local'
|
||||
|
|
|
|||
Loading…
Reference in New Issue