【招生小程序】 优化# 主账号:团队业务逻辑处理
parent
839a51027c
commit
329ad167a5
|
@ -17,71 +17,40 @@
|
||||||
lineColor="#0E66FB"
|
lineColor="#0E66FB"
|
||||||
@change="handleChangeTab"
|
@change="handleChangeTab"
|
||||||
></u-tabs>
|
></u-tabs>
|
||||||
<TTable :columns="columns" :data="data">
|
<template v-if="loading && !data.length">
|
||||||
<template #index="scope">
|
<view class="min-h-[200rpx] flex justify-center items-center">
|
||||||
<text class="px-[16rpx] py-[6rpx] rounded-[4px]" :style="rankStyle(scope.row)">
|
<u-loading-icon></u-loading-icon>
|
||||||
{{ scope.row }}
|
</view>
|
||||||
</text>
|
</template>
|
||||||
</template>
|
<template v-if="!loading && data.length > 0">
|
||||||
</TTable>
|
<TTable :columns="columns" :data="data">
|
||||||
|
<template #index="scope">
|
||||||
|
<text class="px-[16rpx] py-[6rpx] rounded-[4px]" :style="rankStyle(scope.row)">
|
||||||
|
{{ scope.row }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
|
</TTable>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, shallowRef } from 'vue'
|
import { useRank } from '@/hooks/useCommon'
|
||||||
|
import { watch } from 'vue'
|
||||||
|
|
||||||
const tabs = shallowRef([
|
const { tabs, activeTab, columns, data, loading, rankStyle, handleChangeTab, fetchData } = useRank({
|
||||||
{ name: '电销业绩排行榜', value: 1 },
|
width: 160
|
||||||
{ name: '招生业绩排行榜', value: 2 }
|
})
|
||||||
])
|
|
||||||
const activeTab = ref(1)
|
|
||||||
const totalLabel = computed(() => (activeTab.value == 1 ? '意向' : '成交'))
|
|
||||||
|
|
||||||
const handleChangeTab = item => {
|
watch(
|
||||||
activeTab.value = item.value
|
() => activeTab.value,
|
||||||
generateColumns()
|
(val: number) => {
|
||||||
}
|
console.log(val)
|
||||||
const generateColumns = () => {
|
|
||||||
return [
|
|
||||||
{ name: 'index', label: '排名', slot: true },
|
|
||||||
{ name: 'name', label: '姓名' },
|
|
||||||
{ name: 'total', label: totalLabel.value + '客户数', width: 160, align: 'right' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = ref(generateColumns())
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
index: 1,
|
|
||||||
name: '王小虎1789789789789789',
|
|
||||||
total: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 2,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 3,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 4,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const rankStyle = computed(() => row => {
|
|
||||||
const styleMap: Record<number, string> = {
|
|
||||||
'1': '#FCAE3C',
|
|
||||||
'2': '#BCC1D8',
|
|
||||||
'3': '#EEB286'
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
color: row >= 4 ? '#3d3d3d' : '#fff',
|
|
||||||
background: styleMap[row]
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
fetchData()
|
||||||
|
defineExpose({
|
||||||
|
fetchData
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -149,7 +149,7 @@ export function getRangeDate(v) {
|
||||||
// 本周
|
// 本周
|
||||||
} else if (v === '-7') {
|
} else if (v === '-7') {
|
||||||
const weekStart = new Date(nowYear, nowMonth, nowDay - nowWeekDay + 1)
|
const weekStart = new Date(nowYear, nowMonth, nowDay - nowWeekDay + 1)
|
||||||
const weekEnd = new Date(nowTime + oneDay) // 今日
|
const weekEnd = new Date(nowYear, nowMonth, nowDay - nowWeekDay + 7) // 本周日
|
||||||
dateRange.start = formatTime(weekStart, 'y-m-d')
|
dateRange.start = formatTime(weekStart, 'y-m-d')
|
||||||
dateRange.end = formatTime(weekEnd, 'y-m-d')
|
dateRange.end = formatTime(weekEnd, 'y-m-d')
|
||||||
// 上周
|
// 上周
|
||||||
|
@ -161,7 +161,7 @@ export function getRangeDate(v) {
|
||||||
// 本月
|
// 本月
|
||||||
} else if (v === '-30') {
|
} else if (v === '-30') {
|
||||||
const monthStart = new Date(nowYear, nowMonth, 1)
|
const monthStart = new Date(nowYear, nowMonth, 1)
|
||||||
const monthEnd = new Date(nowTime + oneDay)
|
const monthEnd = new Date(nowYear, nowMonth + 1, 0) //本月
|
||||||
dateRange.start = formatTime(monthStart, 'y-m-d')
|
dateRange.start = formatTime(monthStart, 'y-m-d')
|
||||||
dateRange.end = formatTime(monthEnd, 'y-m-d')
|
dateRange.end = formatTime(monthEnd, 'y-m-d')
|
||||||
// 上月
|
// 上月
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
<template>
|
||||||
|
<view class="da-dropdown-daterange-box">
|
||||||
|
<view class="da-dropdown-daterange">
|
||||||
|
<view class="da-dropdown-daterange--date" :class="daterange.start ? 'is-actived' : ''">
|
||||||
|
<picker mode="date" :value="daterange.start" @change="handleStartDate">
|
||||||
|
{{ daterange.start || '请选择日期' }}
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
<view class="da-dropdown-daterange--separate">至</view>
|
||||||
|
<view class="da-dropdown-daterange--date" :class="daterange.end ? 'is-actived' : ''">
|
||||||
|
<picker
|
||||||
|
mode="date"
|
||||||
|
:value="daterange.end"
|
||||||
|
:disabled="!daterange.start"
|
||||||
|
:start="daterange.start"
|
||||||
|
@change="handleEndDate"
|
||||||
|
>
|
||||||
|
{{ daterange.end || '请选择日期' }}
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="da-dropdown-daterange-tags" v-if="dropdownItem.showQuick">
|
||||||
|
<block v-for="(tag, tagi) in dateTagList" :key="tagi">
|
||||||
|
<view
|
||||||
|
class="da-dropdown-tag"
|
||||||
|
:class="datetag === tag.value ? 'is-actived' : ''"
|
||||||
|
@click="handleTagDate(tag.value)"
|
||||||
|
>
|
||||||
|
<text class="da-dropdown-tag--text">{{ tag.label }}</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<dropdown-footer @reset="handleReset" @confirm="handleConfirm" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, watch } from 'vue'
|
||||||
|
import { deepClone, getRangeDate } from '@/components/da-dropdown/utils'
|
||||||
|
import picker from './picker.vue'
|
||||||
|
import dropdownFooter from '../widgets/admin/dropdown-footer.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { picker, dropdownFooter },
|
||||||
|
props: {
|
||||||
|
dropdownItem: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
dropdownIndex: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['success', 'reset', 'confirm'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const daterange = ref(null)
|
||||||
|
const datetag = ref('')
|
||||||
|
const dateTagList = ref([
|
||||||
|
{ value: '-7', label: '本周' },
|
||||||
|
{ value: '-14', label: '上周' },
|
||||||
|
{ value: '-30', label: '本月' },
|
||||||
|
{ value: '-60', label: '上月' },
|
||||||
|
// { value: '-1', label: '昨日' },
|
||||||
|
{ value: '7', label: '近7天' },
|
||||||
|
{ value: '15', label: '近15天' },
|
||||||
|
{ value: '30', label: '近30天' }
|
||||||
|
])
|
||||||
|
|
||||||
|
function initData(dropdownItem, clearValue = false) {
|
||||||
|
const item = deepClone(dropdownItem || null)
|
||||||
|
if (clearValue === true) {
|
||||||
|
daterange.value = {
|
||||||
|
start: '',
|
||||||
|
end: ''
|
||||||
|
}
|
||||||
|
datetag.value = ''
|
||||||
|
} else {
|
||||||
|
daterange.value = {
|
||||||
|
start: item.value?.start || '',
|
||||||
|
end: item.value?.end || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStartDate(item) {
|
||||||
|
daterange.value.start = item.detail.value
|
||||||
|
daterange.value.end = ''
|
||||||
|
datetag.value = ''
|
||||||
|
}
|
||||||
|
function handleEndDate(item) {
|
||||||
|
if (!daterange.value?.start) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
daterange.value.end = item.detail.value
|
||||||
|
datetag.value = ''
|
||||||
|
}
|
||||||
|
function handleTagDate(code) {
|
||||||
|
daterange.value = getRangeDate(code)
|
||||||
|
datetag.value = code
|
||||||
|
}
|
||||||
|
function handleReset() {
|
||||||
|
initData(props.dropdownItem, true)
|
||||||
|
emit('reset')
|
||||||
|
}
|
||||||
|
function handleConfirm() {
|
||||||
|
if (props.dropdownItem?.prop) {
|
||||||
|
const res = { [props.dropdownItem.prop]: deepClone(daterange.value) }
|
||||||
|
emit('success', res, daterange.value, props.dropdownIndex)
|
||||||
|
emit('confirm', daterange.value)
|
||||||
|
} else {
|
||||||
|
console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.dropdownItem,
|
||||||
|
v => {
|
||||||
|
initData(v)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
daterange,
|
||||||
|
datetag,
|
||||||
|
dateTagList,
|
||||||
|
handleStartDate,
|
||||||
|
handleEndDate,
|
||||||
|
handleTagDate,
|
||||||
|
handleReset,
|
||||||
|
handleConfirm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.da-dropdown-daterange-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
// 日期范围
|
||||||
|
.da-dropdown-daterange {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// margin: 24rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
|
||||||
|
&--date {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 66rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 66rpx;
|
||||||
|
color: var(--dropdown-text-color);
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
|
||||||
|
&.is-actived {
|
||||||
|
color: $blue-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--separate {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.da-dropdown-tag {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20rpx 40rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: var(--dropdown-text-color);
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
|
||||||
|
&--text {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-actived {
|
||||||
|
color: $blue-1;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 0;
|
||||||
|
content: '';
|
||||||
|
background-color: $blue-1;
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,222 @@
|
||||||
|
<template>
|
||||||
|
<view class="da-dropdown-picker" v-if="viewCol && viewCol.length">
|
||||||
|
<view class="da-dropdown-picker-inner" v-for="(vc, vci) in viewCol" :key="vci">
|
||||||
|
<scroll-view class="da-dropdown-picker-view" scroll-y>
|
||||||
|
<view
|
||||||
|
class="da-dropdown-picker-item"
|
||||||
|
:class="vr.checked ? 'is-actived' : ''"
|
||||||
|
v-for="(vr, vri) in viewRow[vci]"
|
||||||
|
:key="vri"
|
||||||
|
@click="handleSelect(vr, vci, vri)"
|
||||||
|
>
|
||||||
|
<text class="da-dropdown-picker-item--name">{{ vr.label }}</text>
|
||||||
|
<text
|
||||||
|
class="da-dropdown-picker-item--icon"
|
||||||
|
v-if="vr.children && vr.children.length"
|
||||||
|
></text>
|
||||||
|
<text
|
||||||
|
class="da-dropdown-picker-item--check"
|
||||||
|
v-if="vr.checked && (!vr.children || vr.children.length === 0)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, watch } from 'vue'
|
||||||
|
import { deepClone } from '@/components/da-dropdown/utils'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
dropdownItem: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
dropdownIndex: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['success'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const viewCol = ref([])
|
||||||
|
const viewRow = ref([])
|
||||||
|
|
||||||
|
function checkData(selected, list) {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const k = list[i]
|
||||||
|
for (let j = 0; j < selected.length; j++) {
|
||||||
|
const x = selected[j]
|
||||||
|
if (k.value === x) {
|
||||||
|
k.checked = true
|
||||||
|
viewCol.value.push(k.value)
|
||||||
|
viewRow.value.push(list)
|
||||||
|
if (k.children?.length) {
|
||||||
|
checkData(selected, k.children)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initData(item) {
|
||||||
|
const list = deepClone(item?.options || [])
|
||||||
|
if (list?.length) {
|
||||||
|
if (item.value?.length) {
|
||||||
|
viewCol.value = []
|
||||||
|
viewRow.value = []
|
||||||
|
|
||||||
|
checkData(item.value, list)
|
||||||
|
} else {
|
||||||
|
viewCol.value.push('tmpValue')
|
||||||
|
viewRow.value.push(list)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewCol.value = []
|
||||||
|
viewRow.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelect(item, colIndex, _rowIndex) {
|
||||||
|
let lastItem = false
|
||||||
|
viewCol.value.splice(colIndex)
|
||||||
|
viewCol.value[colIndex] = item.value
|
||||||
|
|
||||||
|
if (viewRow.value[colIndex]?.length) {
|
||||||
|
viewRow.value[colIndex].forEach(k => {
|
||||||
|
k.checked = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
item.checked = true
|
||||||
|
const list = item?.children || null
|
||||||
|
|
||||||
|
if (list?.length) {
|
||||||
|
viewCol.value[colIndex + 1] = 'tmpValue'
|
||||||
|
viewRow.value[colIndex + 1] = list
|
||||||
|
lastItem = false
|
||||||
|
} else {
|
||||||
|
console.warn('最后一项', item)
|
||||||
|
lastItem = true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (viewRow.value[colIndex + 1]?.length) {
|
||||||
|
viewRow.value[colIndex + 1].forEach(k => {
|
||||||
|
k.checked = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('try clean row data', e)
|
||||||
|
// --
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastItem) {
|
||||||
|
if (props.dropdownItem?.prop) {
|
||||||
|
const res = { [props.dropdownItem.prop]: deepClone(viewCol.value) }
|
||||||
|
// emit('success', res, viewCol.value, props.dropdownIndex)
|
||||||
|
} else {
|
||||||
|
console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.dropdownItem,
|
||||||
|
v => {
|
||||||
|
initData(v)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
viewCol,
|
||||||
|
viewRow,
|
||||||
|
|
||||||
|
handleSelect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.da-dropdown-picker {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-view {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
/* #ifdef MP-ALIPAY */
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
+ .da-dropdown-picker-view {
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: var(--dropdown-text-color);
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&--icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
|
||||||
|
font-family: 'da-dropdown-iconfont' !important;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-style: normal;
|
||||||
|
content: '\e643';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--check {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
|
||||||
|
font-family: 'da-dropdown-iconfont' !important;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-style: normal;
|
||||||
|
content: '\e696';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-actived {
|
||||||
|
color: var(--dropdown-theme-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="flex flex-col bg-white rounded-[16rpx] mb-[24rpx]">
|
<view class="flex flex-col bg-white rounded-[16rpx]" :class="addClasses">
|
||||||
<text class="font-bold p-[24rpx]" v-if="title">{{ title }}</text>
|
<text class="font-bold p-[24rpx]" v-if="title">{{ title }}</text>
|
||||||
<view>
|
<view>
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -8,11 +8,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const addClasses = computed(() => {
|
||||||
|
return props.className
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<view class="da-dropdown-footer">
|
||||||
|
<view class="da-dropdown-footer--reset" @click="handleReset()">
|
||||||
|
{{ resetText || '重置' }}
|
||||||
|
</view>
|
||||||
|
<view class="da-dropdown-footer--confirm" @click="handleConfirm()">
|
||||||
|
{{ confirmText || '确定' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PartDropdownFooter',
|
||||||
|
props: {
|
||||||
|
resetText: {
|
||||||
|
type: String,
|
||||||
|
default: '重置'
|
||||||
|
},
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
default: '确定'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['confirm', 'reset'],
|
||||||
|
setup(_, { emit }) {
|
||||||
|
function handleReset() {
|
||||||
|
emit('reset')
|
||||||
|
}
|
||||||
|
function handleConfirm() {
|
||||||
|
emit('confirm')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleReset,
|
||||||
|
handleConfirm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.da-dropdown-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
|
||||||
|
&--reset,
|
||||||
|
&--confirm {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 72rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #555;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 2rpx solid #ccc;
|
||||||
|
border-radius: 66rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--confirm {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
color: #fff;
|
||||||
|
background-color: $blue-1;
|
||||||
|
border-color: $blue-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--reset:hover,
|
||||||
|
&--confirm:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -22,13 +22,16 @@
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<dropdown-footer @reset="handleReset" @confirm="handleConfirm" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { deepClone } from '@/components/da-dropdown/utils'
|
import { deepClone } from '@/components/da-dropdown/utils'
|
||||||
import { defineComponent, ref, watch } from 'vue'
|
import { defineComponent, ref, watch } from 'vue'
|
||||||
|
import dropdownFooter from './dropdown-footer.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: { dropdownFooter },
|
||||||
props: {
|
props: {
|
||||||
dropdownItem: {
|
dropdownItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -38,10 +41,11 @@ export default defineComponent({
|
||||||
type: Number
|
type: Number
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['success'],
|
emits: ['success', 'reset', 'confirm'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const viewCol = ref([])
|
const viewCol = ref([])
|
||||||
const viewRow = ref([])
|
const viewRow = ref([])
|
||||||
|
const selectedNode = ref()
|
||||||
|
|
||||||
function checkData(selected, list) {
|
function checkData(selected, list) {
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
@ -98,8 +102,10 @@ export default defineComponent({
|
||||||
viewRow.value[colIndex + 1] = list
|
viewRow.value[colIndex + 1] = list
|
||||||
lastItem = false
|
lastItem = false
|
||||||
} else {
|
} else {
|
||||||
console.warn('最后一项', item)
|
// console.warn('最后一项', item)
|
||||||
lastItem = true
|
lastItem = true
|
||||||
|
selectedNode.value = item
|
||||||
|
emit('success', item)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -122,6 +128,22 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function handleReset() {
|
||||||
|
viewRow.value.forEach(row => {
|
||||||
|
row.forEach(item => {
|
||||||
|
item.checked = false
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
item.children.forEach(child => {
|
||||||
|
child.checked = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
emit('reset')
|
||||||
|
}
|
||||||
|
function handleConfirm() {
|
||||||
|
emit('confirm', selectedNode.value)
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.dropdownItem,
|
() => props.dropdownItem,
|
||||||
|
@ -136,7 +158,9 @@ export default defineComponent({
|
||||||
viewCol,
|
viewCol,
|
||||||
viewRow,
|
viewRow,
|
||||||
|
|
||||||
handleSelect
|
handleSelect,
|
||||||
|
handleReset,
|
||||||
|
handleConfirm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<card>
|
<card className="mb-3">
|
||||||
<view class="flex flex-col mb-[24rpx]" @click="handleCardClick">
|
<view class="flex flex-col mb-[24rpx]" @click="handleCardClick">
|
||||||
<view class="flex items-center p-[24rpx] gap-[24rpx]">
|
<view class="flex items-center p-[24rpx] gap-[24rpx]">
|
||||||
<view
|
<view
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<card>
|
<card className="mb-3">
|
||||||
<view class="flex flex-col mb-[24rpx]" @click="handleCardClick">
|
<view class="flex flex-col mb-[24rpx]" @click="handleCardClick">
|
||||||
<view class="flex items-center p-[24rpx] gap-[24rpx]">
|
<view class="flex items-center p-[24rpx] gap-[24rpx]">
|
||||||
<view
|
<view
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import positionTabs from './components/position-tabs.vue'
|
import positionTabs from './components/position-tabs.vue'
|
||||||
import DaDropdown from '@/components/da-dropdown/index.vue'
|
import DaDropdown from '@/components/da-dropdown/index.vue'
|
||||||
import DropdownPicker from './components/orga-picker.vue'
|
import DropdownPicker from '../orga-picker.vue'
|
||||||
import telesaleCard from './components/telesale-card.vue'
|
import telesaleCard from './components/telesale-card.vue'
|
||||||
import recruitsaleCard from './components/recruitsale-card.vue'
|
import recruitsaleCard from './components/recruitsale-card.vue'
|
||||||
import { useZPaging } from '@/hooks/useZPaging'
|
import { useZPaging } from '@/hooks/useZPaging'
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="px-[32rpx]">
|
<view class="px-[32rpx]">
|
||||||
<card title="线索转化情况分析">
|
<card title="线索转化情况分析">
|
||||||
<view class="px-[32rpx]">
|
<template v-if="loading && !data.length">
|
||||||
<view
|
<view class="min-h-[200rpx] flex justify-center items-center">
|
||||||
class="flex flex-col gap-[16rpx] mb-[24rpx]"
|
<u-loading-icon></u-loading-icon>
|
||||||
v-for="(item, index) in data"
|
|
||||||
:key="`unique_${index}`"
|
|
||||||
>
|
|
||||||
<text class="text-gray5">{{ item.label }}</text>
|
|
||||||
<u-line-progress
|
|
||||||
:percentage="item.value"
|
|
||||||
activeColor="#5783FE"
|
|
||||||
shape="square"
|
|
||||||
></u-line-progress>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</template>
|
||||||
|
<template v-if="!loading && data.length > 0">
|
||||||
|
<view class="px-[32rpx]">
|
||||||
|
<view
|
||||||
|
class="flex flex-col gap-[16rpx] mb-[24rpx]"
|
||||||
|
v-for="(item, index) in data"
|
||||||
|
:key="`unique_${index}`"
|
||||||
|
>
|
||||||
|
<text class="text-gray5">{{ item.label }}</text>
|
||||||
|
<u-line-progress
|
||||||
|
:percentage="item.value"
|
||||||
|
activeColor="#5783FE"
|
||||||
|
shape="square"
|
||||||
|
></u-line-progress>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
@ -22,13 +29,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
const data = ref([
|
|
||||||
{ label: '待领取', value: 28 },
|
interface ICluseStatus {
|
||||||
{ label: '转化中', value: 12 },
|
label: string
|
||||||
{ label: '已添加', value: 53 },
|
value: number
|
||||||
{ label: '异常待处理', value: 56 },
|
}
|
||||||
{ label: '已成交', value: 12 },
|
const data = ref<ICluseStatus[]>([])
|
||||||
{ label: '已战败', value: 100 }
|
const loading = ref(false)
|
||||||
])
|
const fetchData = () => {
|
||||||
|
loading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
data.value = [
|
||||||
|
{ label: '待领取', value: 28 },
|
||||||
|
{ label: '转化中', value: 12 },
|
||||||
|
{ label: '已添加', value: 53 },
|
||||||
|
{ label: '异常待处理', value: 56 },
|
||||||
|
{ label: '已成交', value: 12 },
|
||||||
|
{ label: '已战败', value: 100 }
|
||||||
|
]
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
fetchData
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,39 +1,63 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="px-[32rpx]">
|
<view class="px-[32rpx]">
|
||||||
<card v-for="(item, index) in data" :key="`unique_${index}`" :title="item.name">
|
<template v-if="loading && !data.length">
|
||||||
<view class="flex bg-gray3 mx-[24rpx] mb-[24rpx]">
|
<view class="min-h-[200rpx] flex justify-center items-center">
|
||||||
<view
|
<u-loading-icon></u-loading-icon>
|
||||||
v-for="(itemy, indey) in item.children"
|
|
||||||
:key="indey"
|
|
||||||
class="flex-1 flex flex-col gap-[12rpx] justify-center items-center py-[12rpx]"
|
|
||||||
>
|
|
||||||
<text class="text-muted">{{ itemy.label }}</text>
|
|
||||||
<text class="font-bold">{{ itemy.value }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
|
<card v-for="(item, index) in data" :key="`unique_${index}`" :title="item.name">
|
||||||
|
<template v-if="!loading && data.length > 0">
|
||||||
|
<view class="flex bg-gray3 mx-[24rpx] mb-[24rpx]">
|
||||||
|
<view
|
||||||
|
v-for="(itemy, indey) in item.children"
|
||||||
|
:key="indey"
|
||||||
|
class="flex-1 flex flex-col gap-[12rpx] justify-center items-center py-[12rpx]"
|
||||||
|
>
|
||||||
|
<text class="text-muted">{{ itemy.label }}</text>
|
||||||
|
<text class="font-bold">{{ itemy.value }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
const data = [
|
|
||||||
{
|
interface IConvertedData {
|
||||||
name: '湛江团队',
|
name: string
|
||||||
children: [
|
children: Array<{ label: string; value: string }>
|
||||||
{ label: '线索', value: '52个' },
|
}
|
||||||
{ label: '成交客户', value: '52个' },
|
const data = ref<IConvertedData[]>([])
|
||||||
{ label: '转化率', value: '15%' }
|
const loading = ref(false)
|
||||||
|
const fetchData = () => {
|
||||||
|
loading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
data.value = [
|
||||||
|
{
|
||||||
|
name: '湛江团队',
|
||||||
|
children: [
|
||||||
|
{ label: '线索', value: '52个' },
|
||||||
|
{ label: '成交客户', value: '52个' },
|
||||||
|
{ label: '转化率', value: '15%' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '广州团队',
|
||||||
|
children: [
|
||||||
|
{ label: '线索', value: '52个' },
|
||||||
|
{ label: '成交客户', value: '52个' },
|
||||||
|
{ label: '转化率', value: '15%' }
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
loading.value = false
|
||||||
{
|
}, 300)
|
||||||
name: '广州团队',
|
}
|
||||||
children: [
|
defineExpose({
|
||||||
{ label: '线索', value: '52个' },
|
fetchData
|
||||||
{ label: '成交客户', value: '52个' },
|
})
|
||||||
{ label: '转化率', value: '15%' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="flex flex-col gap-[24rpx] px-[32rpx]">
|
<view class="flex flex-col gap-[24rpx] px-[32rpx]">
|
||||||
<card title="数据简报">
|
<card title="数据简报">
|
||||||
<view class="flex flex-wrap">
|
<template v-if="loading && !data.length">
|
||||||
<view
|
<view class="min-h-[200rpx] flex justify-center items-center">
|
||||||
class="flex flex-col w-1/3 justify-center items-center gap-[12rpx] mb-[20rpx]"
|
<u-loading-icon></u-loading-icon>
|
||||||
v-for="(item, index) in data"
|
|
||||||
:key="`unique_${index}`"
|
|
||||||
>
|
|
||||||
<text class="text-muted">{{ item.label }}</text>
|
|
||||||
<text class="font-bold text-[40rpx]">{{ item.value }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</template>
|
||||||
|
<template v-else-if="!loading && data.length > 0">
|
||||||
|
<view class="flex flex-wrap">
|
||||||
|
<view
|
||||||
|
class="flex flex-col w-1/3 justify-center items-center gap-[12rpx] mb-[20rpx]"
|
||||||
|
v-for="(item, index) in data"
|
||||||
|
:key="`unique_${index}`"
|
||||||
|
>
|
||||||
|
<text class="text-muted">{{ item.label }}</text>
|
||||||
|
<text class="font-bold text-[40rpx]">{{ item.value }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
@ -19,13 +26,28 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
|
|
||||||
const data = ref([
|
interface DataItem {
|
||||||
{ label: '新增跟进', value: 368 },
|
label: string
|
||||||
{ label: '新增客户', value: 368 },
|
value: number
|
||||||
{ label: '成交客户', value: 368 },
|
}
|
||||||
{ label: '转化中客户', value: 368 },
|
const loading = ref(false)
|
||||||
{ label: '异常待处理', value: 368 },
|
const data = ref<DataItem[]>([])
|
||||||
{ label: '战败客户', value: 368 }
|
const fetchData = () => {
|
||||||
])
|
loading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
data.value = [
|
||||||
|
{ label: '新增跟进', value: 368 },
|
||||||
|
{ label: '新增客户', value: 368 },
|
||||||
|
{ label: '成交客户', value: 368 },
|
||||||
|
{ label: '转化中客户', value: 368 },
|
||||||
|
{ label: '异常待处理', value: 368 },
|
||||||
|
{ label: '战败客户', value: 368 }
|
||||||
|
]
|
||||||
|
loading.value = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
fetchData
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|
|
@ -18,84 +18,56 @@
|
||||||
lineColor="#0E66FB"
|
lineColor="#0E66FB"
|
||||||
@change="handleChangeTab"
|
@change="handleChangeTab"
|
||||||
></u-tabs>
|
></u-tabs>
|
||||||
<TTable :columns="columns" :data="data">
|
<template v-if="loading && !data.length">
|
||||||
<template #index="scope">
|
<view class="min-h-[200rpx] flex justify-center items-center">
|
||||||
<text class="px-[16rpx] py-[6rpx] rounded-[4px]" :style="rankStyle(scope.row)">
|
<u-loading-icon></u-loading-icon>
|
||||||
{{ scope.row }}
|
</view>
|
||||||
</text>
|
</template>
|
||||||
</template>
|
<template v-if="!loading && data.length > 0">
|
||||||
</TTable>
|
<TTable :columns="columns" :data="data">
|
||||||
<view
|
<template #index="scope">
|
||||||
class="flex justify-center py-[20rpx] border-t border-solid border-border"
|
<text
|
||||||
@click="handleMore"
|
class="px-[16rpx] py-[6rpx] rounded-[4px]"
|
||||||
>
|
:style="rankStyle(scope.row)"
|
||||||
<text>查看更多</text>
|
>
|
||||||
</view>
|
{{ scope.row }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
|
</TTable>
|
||||||
|
<view
|
||||||
|
class="flex justify-center py-[20rpx] border-t border-solid border-border"
|
||||||
|
@click="handleMore"
|
||||||
|
>
|
||||||
|
<text>查看更多</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, shallowRef } from 'vue'
|
import { watch } from 'vue'
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
import { computed } from 'vue'
|
import { useRank } from '@/hooks/useCommon'
|
||||||
|
|
||||||
const activeTab = ref(1)
|
const { tabs, activeTab, columns, data, loading, rankStyle, handleChangeTab, fetchData } = useRank({
|
||||||
const tabs = shallowRef([
|
width: 120
|
||||||
{ name: '电销TOP5', value: 1 },
|
|
||||||
{ name: '招生TOP5', value: 2 }
|
|
||||||
])
|
|
||||||
const totalLabel = computed(() => (activeTab.value == 1 ? '意向' : '成交'))
|
|
||||||
const handleChangeTab = item => {
|
|
||||||
activeTab.value = item.value
|
|
||||||
columns.value = generateColumns()
|
|
||||||
}
|
|
||||||
const generateColumns = () => {
|
|
||||||
return [
|
|
||||||
{ name: 'index', label: '排名', slot: true },
|
|
||||||
{ name: 'name', label: '姓名' },
|
|
||||||
{ name: 'total', label: totalLabel.value + '客户数', width: 120, align: 'right' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = ref(generateColumns())
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
index: 1,
|
|
||||||
name: '王小虎1789789789789789',
|
|
||||||
total: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 2,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 3,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 4,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const rankStyle = computed(() => row => {
|
|
||||||
const styleMap: Record<number, string> = {
|
|
||||||
'1': '#FCAE3C',
|
|
||||||
'2': '#BCC1D8',
|
|
||||||
'3': '#EEB286'
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
color: row >= 4 ? '#3d3d3d' : '#fff',
|
|
||||||
background: styleMap[row]
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => activeTab.value,
|
||||||
|
(val: number) => {
|
||||||
|
console.log(val)
|
||||||
|
}
|
||||||
|
)
|
||||||
const handleMore = () => {
|
const handleMore = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/bundle/pages/rank-list/index'
|
url: '/bundle/pages/rank-list/index'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
fetchData
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,16 +1,108 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="flex flex-col gap-[24rpx] mb-[20rpx]">
|
<view class="flex flex-col gap-[24rpx] mb-[20rpx]">
|
||||||
<data-overview />
|
<view class="bg-white">
|
||||||
<converted-overview />
|
<u-dropdown ref="uDropdownRef" menu-icon="arrow-down-fill" :isFlex="false">
|
||||||
<rank />
|
<u-dropdown-item title="组织">
|
||||||
<clue-status />
|
<view class="bg-white">
|
||||||
|
<DropdownPicker
|
||||||
|
:dropdownItem="dropdownMenuList"
|
||||||
|
@reset="() => handleReset('organization')"
|
||||||
|
@confirm="value => handleConfirm('organization', value)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</u-dropdown-item>
|
||||||
|
<u-dropdown-item title="日期">
|
||||||
|
<view class="bg-white p-[20rpx]">
|
||||||
|
<dateDropdownPicker
|
||||||
|
:dropdownItem="dropdownMenuList2"
|
||||||
|
@reset="() => handleReset('date')"
|
||||||
|
@confirm="value => handleConfirm('date', value)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</u-dropdown-item>
|
||||||
|
</u-dropdown>
|
||||||
|
</view>
|
||||||
|
<data-overview ref="dataOverviewRef" />
|
||||||
|
<converted-overview ref="convertedOverviewRef" />
|
||||||
|
<rank ref="rankRef" />
|
||||||
|
<clue-status ref="clueStatusRef" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
import dataOverview from './components/data-overview.vue'
|
import dataOverview from './components/data-overview.vue'
|
||||||
import convertedOverview from './components/converted-overview.vue'
|
import convertedOverview from './components/converted-overview.vue'
|
||||||
import rank from './components/rank.vue'
|
import rank from './components/rank.vue'
|
||||||
import clueStatus from './components/clue-status.vue'
|
import clueStatus from './components/clue-status.vue'
|
||||||
|
import DropdownPicker from '../orga-picker.vue'
|
||||||
|
import dateDropdownPicker from '@/components/date-dropdown/daterange.vue'
|
||||||
|
|
||||||
|
const clueStatusRef = ref<InstanceType<typeof clueStatus>>()
|
||||||
|
const rankRef = ref<InstanceType<typeof rank>>()
|
||||||
|
const convertedOverviewRef = ref<InstanceType<typeof convertedOverview>>()
|
||||||
|
const dataOverviewRef = ref<InstanceType<typeof dataOverview>>()
|
||||||
|
const uDropdownRef = ref()
|
||||||
|
const dropdownMenuList = [
|
||||||
|
{
|
||||||
|
label: '级联X1',
|
||||||
|
value: '1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '级联X11',
|
||||||
|
value: '11'
|
||||||
|
},
|
||||||
|
{ label: '级联X12', value: '12' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '级联X2',
|
||||||
|
value: '2',
|
||||||
|
children: [
|
||||||
|
{ label: '级联X21', value: '21' },
|
||||||
|
{ label: '级联X22', value: '22' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const dropdownMenuList2 = {
|
||||||
|
showQuick: true,
|
||||||
|
title: '日期范围',
|
||||||
|
type: 'daterange',
|
||||||
|
prop: 'god6'
|
||||||
|
// 默认选中 2022-01-01到2022-02-01
|
||||||
|
// value: { start: '2022-01-01', end: '2022-02-01' },
|
||||||
|
}
|
||||||
|
const handleConfirm = (type: string, item) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'organization':
|
||||||
|
console.log(item)
|
||||||
|
break
|
||||||
|
case 'date':
|
||||||
|
console.log(item)
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleReset = (type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'organization':
|
||||||
|
break
|
||||||
|
case 'date':
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const closeDropDown = () => {
|
||||||
|
uDropdownRef.value.close()
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
dataOverviewRef.value?.fetchData()
|
||||||
|
convertedOverviewRef.value?.fetchData()
|
||||||
|
rankRef.value?.fetchData()
|
||||||
|
clueStatusRef.value?.fetchData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { apiCluseDetail } from '@/api/clue'
|
import { apiCluseDetail } from '@/api/clue'
|
||||||
import { ref } from 'vue'
|
import { computed, ref, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 获取线索详情
|
// 获取线索详情
|
||||||
export function useClueDetail() {
|
export function useClueDetail() {
|
||||||
|
@ -19,3 +19,98 @@ export function useClueDetail() {
|
||||||
fetchClueDetail
|
fetchClueDetail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IRank {
|
||||||
|
index: number
|
||||||
|
name: string
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
interface IRankTab {
|
||||||
|
name: string
|
||||||
|
value: number
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
// 主账号排行榜
|
||||||
|
export function useRank({ width }: { width: number }) {
|
||||||
|
const tabs = ref<IRankTab[]>([])
|
||||||
|
const activeTab = ref()
|
||||||
|
const loading = ref(false)
|
||||||
|
const data = ref<IRank[]>([])
|
||||||
|
const totalLabel = computed(() => (activeTab.value == 5 ? '意向' : '成交'))
|
||||||
|
const rankStyle = computed(() => row => {
|
||||||
|
const styleMap: Record<number, string> = {
|
||||||
|
'1': '#FCAE3C',
|
||||||
|
'2': '#BCC1D8',
|
||||||
|
'3': '#EEB286'
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
color: row >= 4 ? '#3d3d3d' : '#fff',
|
||||||
|
background: styleMap[row]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleChangeTab = item => {
|
||||||
|
activeTab.value = item.id
|
||||||
|
generateColumns()
|
||||||
|
}
|
||||||
|
const generateColumns = () => {
|
||||||
|
return [
|
||||||
|
{ name: 'index', label: '排名', slot: true },
|
||||||
|
{ name: 'name', label: '姓名' },
|
||||||
|
{ name: 'total', label: totalLabel.value + '客户数', width, align: 'right' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = ref(generateColumns())
|
||||||
|
const fetchTabs = () => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
tabs.value = [
|
||||||
|
{ name: '电销业绩排行榜', value: 1, id: 5 },
|
||||||
|
{ name: '招生业绩排行榜', value: 2, id: 6 }
|
||||||
|
]
|
||||||
|
if (tabs.value.length > 0) activeTab.value = tabs.value[0]
|
||||||
|
resolve(tabs.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const fetchData = async () => {
|
||||||
|
await fetchTabs()
|
||||||
|
console.log(activeTab.value)
|
||||||
|
loading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
data.value = [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
name: '王小虎1789789789789789',
|
||||||
|
total: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
name: '王小虎2',
|
||||||
|
total: 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
name: '王小虎2',
|
||||||
|
total: 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
name: '王小虎2',
|
||||||
|
total: 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
loading.value = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetchData,
|
||||||
|
rankStyle,
|
||||||
|
activeTab,
|
||||||
|
columns,
|
||||||
|
tabs,
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
handleChangeTab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
lineColor="#0E66FB"
|
lineColor="#0E66FB"
|
||||||
@change="handleChangeTab"
|
@change="handleChangeTab"
|
||||||
></u-tabs>
|
></u-tabs>
|
||||||
<view class="flex-1 bg-gray3 pt-[32rpx]">
|
<view class="flex-1 bg-gray3">
|
||||||
<admin-team v-if="activeTab === AdminTabEnum.TEAM" />
|
<admin-team v-if="activeTab === AdminTabEnum.TEAM" />
|
||||||
<personally v-if="activeTab === AdminTabEnum.PERSONALLY" />
|
<personally v-if="activeTab === AdminTabEnum.PERSONALLY" />
|
||||||
</view>
|
</view>
|
||||||
|
@ -22,7 +22,7 @@ import { AdminTabEnum } from '@/enums'
|
||||||
import adminTeam from '@/components/widgets/admin/team/index.vue'
|
import adminTeam from '@/components/widgets/admin/team/index.vue'
|
||||||
import personally from '@/components/widgets/admin/personally/index.vue'
|
import personally from '@/components/widgets/admin/personally/index.vue'
|
||||||
|
|
||||||
const activeTab = ref(AdminTabEnum.PERSONALLY)
|
const activeTab = ref(AdminTabEnum.TEAM)
|
||||||
const tabs = shallowRef([
|
const tabs = shallowRef([
|
||||||
{ name: '团队', value: AdminTabEnum.TEAM },
|
{ name: '团队', value: AdminTabEnum.TEAM },
|
||||||
{ name: '个人', value: AdminTabEnum.PERSONALLY }
|
{ name: '个人', value: AdminTabEnum.PERSONALLY }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<TContainer></TContainer>
|
<TProfile :isShow="false"></TProfile>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts"></script>
|
||||||
<style scoped></style>
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
|
|
|
@ -56,6 +56,10 @@ export const props = defineMixin({
|
||||||
menuIconSize: {
|
menuIconSize: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
default: 14
|
default: 14
|
||||||
|
},
|
||||||
|
isFlex: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
class="u-dropdown__menu__item"
|
class="u-dropdown__menu__item"
|
||||||
|
:style="itemStyle"
|
||||||
v-for="(item, index) in menuList"
|
v-for="(item, index) in menuList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@tap.stop="menuClick(index)"
|
@tap.stop="menuClick(index)"
|
||||||
|
@ -122,6 +123,12 @@ export default {
|
||||||
style['transition-duration'] = this.duration / 1000 + 's'
|
style['transition-duration'] = this.duration / 1000 + 's'
|
||||||
style.borderRadius = `0 0 ${addUnit(this.borderRadius)} ${addUnit(this.borderRadius)}`
|
style.borderRadius = `0 0 ${addUnit(this.borderRadius)} ${addUnit(this.borderRadius)}`
|
||||||
return style
|
return style
|
||||||
|
},
|
||||||
|
itemStyle() {
|
||||||
|
let style = {}
|
||||||
|
style.flex = this.isFlex ? 1 : ''
|
||||||
|
style.minWidth = this.isFlex ? '' : '25%'
|
||||||
|
return style
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -232,7 +239,7 @@ export default {
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
flex: 1;
|
// flex: 1;
|
||||||
@include flex;
|
@include flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
Loading…
Reference in New Issue