feat(销售页面): 添加模块显示控制功能及周期分析组件
refactor(StatisticData): 简化指标名称显示 style(UserDropdown): 添加显示设置弹窗样式
This commit is contained in:
@@ -25,6 +25,13 @@
|
||||
</svg>
|
||||
修改密码
|
||||
</div>
|
||||
<div class="dropdown-item" @click="handleDisplaySettings">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" style="margin-right: 8px;">
|
||||
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z" fill="currentColor"/>
|
||||
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z" fill="currentColor"/>
|
||||
</svg>
|
||||
显示设置
|
||||
</div>
|
||||
<div class="dropdown-item logout-item" @click="handleLogout">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" style="margin-right: 8px;">
|
||||
<path d="M6 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H6zM5 3a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V3z" fill="currentColor"/>
|
||||
@@ -148,6 +155,49 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 显示设置弹窗 -->
|
||||
<div v-if="showDisplayModal" class="display-modal-overlay" @click="cancelDisplaySettings">
|
||||
<div class="display-modal" @click.stop>
|
||||
<div class="display-modal-header">
|
||||
<h2>显示设置</h2>
|
||||
<p>选择要显示的模块</p>
|
||||
</div>
|
||||
|
||||
<div class="display-modal-body">
|
||||
<div class="checkbox-group">
|
||||
<label v-for="(visible, key) in localCardVisibility" :key="key" class="checkbox-item">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="localCardVisibility[key]"
|
||||
:disabled="displayLoading"
|
||||
/>
|
||||
<span class="checkbox-label">{{ getCardDisplayName(key) }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="display-modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-cancel"
|
||||
@click="cancelDisplaySettings"
|
||||
:disabled="displayLoading"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-confirm"
|
||||
@click="handleDisplaySubmit"
|
||||
:disabled="displayLoading"
|
||||
>
|
||||
<span v-if="displayLoading" class="loading-spinner"></span>
|
||||
{{ displayLoading ? '应用中...' : '确认应用' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 退出登录确认弹窗 -->
|
||||
<div v-if="showLogoutModal" class="logout-modal-overlay" @click="cancelLogout">
|
||||
<div class="logout-modal" @click.stop>
|
||||
@@ -166,11 +216,28 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import http from '@/utils/https'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
cardVisibility: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
timeline: true,
|
||||
rawData: true,
|
||||
customerDetail: true,
|
||||
analytics: true,
|
||||
weekAnalysis: true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['update-card-visibility'])
|
||||
|
||||
// 路由实例
|
||||
const router = useRouter()
|
||||
|
||||
@@ -192,6 +259,26 @@ const passwordForm = ref({
|
||||
newPassword: ''
|
||||
}) // 修改密码表单数据
|
||||
const passwordLoading = ref(false) // 修改密码加载状态
|
||||
const showDisplayModal = ref(false) // 显示设置弹窗显示状态
|
||||
const displayLoading = ref(false) // 显示设置加载状态
|
||||
const localCardVisibility = reactive({}) // 本地卡片显示状态
|
||||
|
||||
// 监听props变化,同步到本地状态
|
||||
watch(() => props.cardVisibility, (newVal) => {
|
||||
Object.assign(localCardVisibility, newVal)
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
// 获取卡片显示名称
|
||||
const getCardDisplayName = (key) => {
|
||||
const nameMap = {
|
||||
timeline: '销售时间线',
|
||||
rawData: '原始数据',
|
||||
customerDetail: '客户详情',
|
||||
analytics: '数据分析',
|
||||
weekAnalysis: '周期分析'
|
||||
}
|
||||
return nameMap[key] || key
|
||||
}
|
||||
|
||||
// 切换下拉菜单显示状态
|
||||
const toggleDropdown = () => {
|
||||
@@ -309,6 +396,39 @@ const cancelPasswordChange = () => {
|
||||
passwordForm.value.newPassword = ''
|
||||
}
|
||||
|
||||
// 显示设置
|
||||
const handleDisplaySettings = () => {
|
||||
console.log('显示设置')
|
||||
showDropdown.value = false
|
||||
showDisplayModal.value = true
|
||||
}
|
||||
|
||||
// 显示设置处理函数
|
||||
const handleDisplaySubmit = async () => {
|
||||
displayLoading.value = true
|
||||
|
||||
try {
|
||||
// 发送事件给父组件更新卡片显示状态
|
||||
emit('update-card-visibility', { ...localCardVisibility })
|
||||
|
||||
// 模拟异步操作
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
showDisplayModal.value = false
|
||||
} catch (error) {
|
||||
console.error('显示设置失败:', error)
|
||||
} finally {
|
||||
displayLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 取消显示设置
|
||||
const cancelDisplaySettings = () => {
|
||||
showDisplayModal.value = false
|
||||
// 恢复到原始状态
|
||||
Object.assign(localCardVisibility, props.cardVisibility)
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
showDropdown.value = false
|
||||
@@ -655,6 +775,142 @@ const cancelLogout = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示设置弹窗样式 */
|
||||
.display-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.display-modal {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
min-width: 400px;
|
||||
max-width: 500px;
|
||||
animation: modalSlideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.display-modal-header {
|
||||
padding: 24px 24px 16px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.display-modal-header h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.display-modal-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.display-modal-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.checkbox-item:hover {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
accent-color: #667eea;
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"]:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.display-modal-footer {
|
||||
padding: 16px 24px 24px 24px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-cancel,
|
||||
.display-modal-footer .btn-confirm {
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
min-width: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-cancel {
|
||||
background: #f8fafc;
|
||||
color: #64748b;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-cancel:hover:not(:disabled) {
|
||||
background: #f1f5f9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-confirm {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-confirm:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.display-modal-footer .btn-cancel:disabled,
|
||||
.display-modal-footer .btn-confirm:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 修改密码弹窗样式 */
|
||||
.password-modal-overlay {
|
||||
position: fixed;
|
||||
|
||||
Reference in New Issue
Block a user