feat: 接口对接

master
xjs 2025-05-23 11:39:05 +08:00
parent 2254df4e95
commit eba5899cd6
30 changed files with 1100 additions and 705 deletions

View File

@ -0,0 +1,39 @@
// timer.worker.js
let timer = null;
let interval = 1000;
self.addEventListener('message', (e) => {
switch (e.data.command) {
case 'start':
startTimer(e.data.interval);
break;
case 'stop':
stopTimer();
break;
case 'update':
interval = e.data.interval;
break;
}
});
// 关闭时的清理逻辑
self.addEventListener('close', () => {
stopTimer();
});
function startTimer(newInterval) {
interval = newInterval || interval;
stopTimer(); // 先停止已有定时器
self.postMessage({ type: 'tick', timestamp:performance.now() });
timer = setInterval(() => {
const timestamp = performance.now();
self.postMessage({ type: 'tick', timestamp });
}, interval);
}
function stopTimer() {
if (timer) {
clearInterval(timer);
timer = null;
}
}

View File

@ -1,205 +0,0 @@
import axios, { AxiosRequestConfig } from 'axios'
import { handleUrl } from './encryptUrl'
// 自定义判断元素类型JS
function toType(obj: any): string {
return {}.toString
.call(obj)
.match(/\s([a-zA-Z]+)/)![1]
.toLowerCase()
}
// 参数过滤函数
function filterNull(o: any) {
for (var key in o) {
if (o[key] === null) {
delete o[key]
}
if (toType(o[key]) === 'string') {
o[key] = o[key].trim()
} else if (toType(o[key]) === 'object') {
o[key] = filterNull(o[key])
} else if (toType(o[key]) === 'array') {
o[key] = filterNull(o[key])
}
}
return o
}
/*
https://cnodejs.org/api/v1 的接口,如果是其他接口
https://cnodejs.org/topic/5378720ed6e2d16149fa16bd
alert
*/
function apiAxios(
method: string,
url: string,
params: null | string | object,
success: any,
failure: any,
unEncrypt: boolean = false // 是否不加密
) {
let contentTypeIsJson = false
if (params && typeof params != 'string') {
params = filterNull(params)
} else contentTypeIsJson = true
axios({
method: method,
url: url,
data: method === 'POST' || method === 'PUT' ? params : null,
params: method === 'GET' || method === 'DELETE' ? params : null,
withCredentials: true,
crossDomain: true,
unEncrypt,
transformRequest: [
function (data) {
if (contentTypeIsJson) return data
let ret = ''
for (let it in data) {
ret +=
encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
},
],
headers: {
'Content-Type': contentTypeIsJson
? 'application/json'
: 'application/x-www-form-urlencoded',
},
} as AxiosRequestConfig<any>)
.then(function (res) {
let response = res.data
if (response.code == 200) {
if (success) {
success(response)
}
} else {
if (failure) {
failure(response)
} else {
if (response.code == 2) {
//错误处理
setTimeout(() => {
location.reload()
}, 1000)
} else {
//错误处理
}
}
}
})
.catch(function (err) {
let res = err.response
console.error(res || err)
if (res) {
// 清楚所有的错误提示
clearTimeout(timeObj)
if (res.data.msg) {
//错误处理
} else {
//错误处理
}
return
}
})
}
let requestCount = 0
let timeObj: NodeJS.Timeout
// http request 拦截器
axios.interceptors.request.use((config) => {
requestCount++
if (requestCount == 1) {
timeObj = setTimeout(() => {
//加载中提示
}, 800)
}
if (
config.data &&
Object.prototype.toString.call(config.data) == '[object FormData]'
) {
config.headers!!['Content-Type'] = 'multipart/form-data;charset=utf-8'
config.transformRequest = [
function (data) {
return data
},
]
}
// 拦截配置有新的配置在这里新增函数处理然后合并config
let _config = handleUrl(config)
config = Object.assign(config, _config)
return config
})
// http response 拦截器
axios.interceptors.response.use((response) => {
requestCount--
if (requestCount === 0) {
setTimeout(() => {
// 关闭所有提示
}, 1500)
clearTimeout(timeObj)
}
return response
},error =>{
// let xhrErrL = { type: "XHRERR", data: error.response };
if (error.response) {
const { status, data } = error.response;
if (status === 422) {
alert(data)
}
}
})
// 返回在vue模板中的调用接口
export default {
get: function (
url: string,
params: string | object | null,
success: any,
failure: any
) {
return apiAxios('GET', url, params, success, failure)
},
post: function (
url: string,
params: string | object,
success: any,
failure: any
) {
return apiAxios('POST', url, params, success, failure)
},
put: function (
url: string,
params: string | object,
success: any,
failure: any
) {
return apiAxios('PUT', url, params, success, failure)
},
delete: function (
url: string,
params: string | object,
success: any,
failure: any
) {
return apiAxios('DELETE', url, params, success, failure)
},
// 不加密的post请求
unEncryptPost: function (
url: string,
params: string | object,
success: any,
failure: any,
) {
return apiAxios('POST', url, params, success, failure,true)
}
}

62
src/api/customFetch.ts Normal file
View File

@ -0,0 +1,62 @@
/*
* @Author: HideInMatrix
* @Date: 2024-07-15
* @LastEditors: error: git config user.name & please set dead value or install git
* @LastEditTime: 2024-09-01
* @Description:
* @FilePath: /free-music-react/src/lib/customFetch.ts
*/
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
interface FetchOptions extends RequestInit {
headers?: Record<string, string>;
}
interface ApiResponse {
result?: unknown;
error?: string;
code?: number;
success?: boolean;
message?:string;
}
const apiClient = (method: HttpMethod) => {
return async (
url: string,
data?: unknown,
options: FetchOptions = {}
): Promise<ApiResponse> => {
const config: FetchOptions = {
method,
...options,
};
if (method !== "GET" && data) {
config.body = JSON.stringify(data);
} else if (method === "GET" && data) {
const _params = [];
for (const [key, value] of Object.entries(data)) {
_params.push(`${key}=${value}`);
}
url += `?${_params.join("&")}`;
}
const response = await fetch(`${url}`, config);
const result = await response.json();
if (!response.ok) {
return {
error: result.message || "Request failed",
code: response.status,
};
}
return result;
};
};
export const getRequest = apiClient("GET");
export const postRequest = apiClient("POST");
export const putRequest = apiClient("PUT");
export const deleteRequest = apiClient("DELETE");

View File

@ -1,12 +0,0 @@
// 对指定请求链接进行加密
import { AxiosRequestConfig } from "axios";
import { useUserStore } from "@/store/user";
export const handleUrl = async (config: (AxiosRequestConfig & {unEncrypt?: boolean})) => {
const userStore = useUserStore()
if(userStore.getToken){
config.headers!!["Authorization"] = `Bearer ${userStore.getToken}`
}
return config;
}

13
src/api/fetchUrl.ts Normal file
View File

@ -0,0 +1,13 @@
const baseUrl = "http://api.screen.jinze.ycymedu.com";
export const getPaymentUrl = () => `${baseUrl}/api/bigScreenData/bigScreenData`;
export const getGainUrl = () => `${baseUrl}/api/bigScreenData/bigScreenCustomerAcq`;
export const getAcqTrend = () => `${baseUrl}/api/bigScreenData/bigScreenAcqTrend`;
export const getSegmentStatic = () => `${baseUrl}/api/bigScreenData/bigScreenStages`;
export const getBigScreenRanking = () => `${baseUrl}/api/bigScreenData/bigScreenRanks`;
export const getSixStatisticsUrl = () => `${baseUrl}/api/bigScreenData/wechatData`

View File

@ -117,11 +117,11 @@ const getSegmentClass = (digit: string, index: number) => {
let timer: number | null = null;
let { hours, minutes, seconds, updateTime } = useDate()
let { hours, minutes, seconds, formateTime } = useDate()
onMounted(() => {
updateTime();
timer = window.setInterval(updateTime, 500);
formateTime();
timer = window.setInterval(formateTime, 500);
});
onUnmounted(() => {

View File

@ -9,7 +9,7 @@ export const useDate = () => {
let timer: number | null = null;
const updateTime = () => {
const formateTime = () => {
const now = new Date();
hours.value = now.getHours().toString().padStart(2, "0");
@ -35,6 +35,6 @@ export const useDate = () => {
day,
weekday, // 返回星期几
timer,
updateTime,
formateTime,
};
};

View File

@ -0,0 +1,59 @@
// 定义函数类型,支持任意参数
type PollingCallback = (...args: any[]) => void;
export const requestArray = new Map<string, { callback: PollingCallback; args?: any[] }>();
// 添加函数到 Map
export const addRequest = (key: string, callback: PollingCallback, args?: any[]) => {
requestArray.set(key, { callback, args });
};
// 从 Map 中移除函数
export const removeRequest = (key: string) => {
requestArray.delete(key);
};
export const runImmediatelyByKey = (key: string) => {
const { callback, args } = requestArray.get(key) || {};
if (callback) {
callback(...(args || []));
}
};
export const runImmediatelyAll = () => {
updateTime.value = new Date().getTime();
requestArray.forEach(({ callback, args }) => {
if (args) {
callback(...args);
}else{
callback()
}
});
};
export let updateTime = ref(0);
export const usePolling = () => {
const pollingWorker = new Worker("/js/polling.worker.js");
pollingWorker.onmessage = (_e: any) => {
// 执行所有存储的函数
updateTime.value = new Date().getTime();
requestArray.forEach(({ callback, args }) => {
if (args) {
callback(...args);
} else {
callback();
}
});
};
onMounted(() => {
pollingWorker.postMessage({ command: "start", interval: 1000 * 60 * 60 });
});
onBeforeUnmount(() => {
pollingWorker.postMessage({ command: "stop" });
// 清理所有存储的函数
requestArray.clear();
});
};

View File

@ -0,0 +1,13 @@
export function convertNumber(n: number): string {
if (n >= 10000) {
const value = n / 10000;
return `${value.toString().replace(/\.0$/, '')}W`;
} else if (n >= 1000) {
const value = n / 1000;
return `${value.toString().replace(/\.0$/, '')}K`;
} else if (n >= 1) {
const value = n / 1000;
return `${value.toString().replace(/0+$/, '').replace(/\.$/, '')}K`;
}
return n.toString();
}

34
src/utils/date.ts Normal file
View File

@ -0,0 +1,34 @@
/**
* "月.日 时:分:秒"
* @param timestamp
* @returns "6.12 12:00:00"
*/
export const formatDatetime = (timestamp: number): string => {
const date = new Date(timestamp);
// 获取月份注意getMonth 返回的月份是从0开始的所以需要+1
const month = date.getMonth() + 1;
// 获取日期
const day = date.getDate();
// 获取小时
const hours = date.getHours().toString().padStart(2, '0');
// 获取分钟
const minutes = date.getMinutes().toString().padStart(2, '0');
// 获取秒数
const seconds = date.getSeconds().toString().padStart(2, '0');
// 组合成最终格式
return `${month}.${day} ${hours}:${minutes}:${seconds}`;
};
/**
*
* @returns
*/
export const getCurrentTimestamp = (): number => {
return Date.now();
};

View File

@ -11,7 +11,7 @@
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
<AskSectionChart />
<AskSectionChart :chart-data="askSectionData.stages || []" />
</div>
</div>
</template>
@ -39,6 +39,9 @@
arrowLeftSvg.value = svg;
};
const askSectionData = inject("askSectionData",ref({stages:[]}))
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();

View File

@ -39,10 +39,28 @@
const columns = [
{ field: "rank", header: "名次", align: "justify-center", width: "68px" },
{ field: "name", header: "姓名", align: "justify-left", width: "100px" },
{ field: "Status", header: "获客人数", align: "justify-center", width: "96px" },
{ field: "total", header: "收费人数", align: "justify-center", width: "96px" },
];
const products = [{ name: "张三" }, { name: "张三" }, { name: "张三" }, { name: "张三" }, { name: "张三" }];
let products = ref<any[]>([]);
const chargingRankingData = inject(
"chargingRankingData",
ref<{
paymentRanks: any[];
}>({
paymentRanks: [],
}),
);
watch(
() => chargingRankingData.value,
() => {
if(chargingRankingData.value.paymentRanks){
products.value = chargingRankingData.value.paymentRanks
}
},
);
const headerLeftSvg = ref("");
const headerRightSvg = ref("");

View File

@ -5,8 +5,8 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10">
<span class="text-[34px] italic">125</span>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{ gainData.toDayAcq || 0 }}</span>
<span class="text-[18px]"></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
@ -17,11 +17,11 @@
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
<div class="w-[127px] h-[104px] top-0 left-0 z-1 absolute pl-[14px] flex flex-col justify-center">
<p class="text-[#C0EEFF] text-[12px]">昨日获客人数</p>
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177</p>
<div class="text-[#FF4E4E] text-[12px] flex items-center">
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{ gainData.yestertDayAcq || 0 }}</p>
<div :class="`${gainDifferentTody > 0 ?'text-[#4AFFA2]':'text-[#FF4E4E]'} text-[12px] flex items-center`">
<span class="">今日较昨日</span>
<SvgIcon name="arrow-up" class="text-[9px]" />
<span>12</span>
<SvgIcon :name="gainDifferentTody > 0 ? 'arrow-up' : 'arrow-down'" class="text-[9px]" />
<span>{{ Math.abs(gainDifferentTody) }}</span>
</div>
</div>
</div>
@ -34,6 +34,7 @@
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
const gainData = inject("gainData",ref({toDayAcq:0,yestertDayAcq:0}))
const groupSvg = ref("");
const getGroupBackgroundSvg = async () => {
@ -41,6 +42,13 @@
groupSvg.value = svg;
};
const gainDifferentTody = computed(() => {
if((Object.prototype.toString.call(gainData.value.toDayAcq) !== "[object Undefined]") && (Object.prototype.toString.call(gainData.value.yestertDayAcq) !== "[object Undefined]")){
return gainData.value.toDayAcq - gainData.value.yestertDayAcq
}else{
return 0;
}
})
const hexagonalBoxSvg = ref("");

View File

@ -5,17 +5,17 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10">
<span class="text-[34px] italic">125</span>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{gainData.acqTotal}}</span>
<span class="text-[18px]"></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
</div>
</div>
<div class="relative ml-[37px] flex flex-col items-center">
<span class="text-[#C0EEFF] text-[12px]">目标值2W</span>
<span class="text-[#C0EEFF] text-[12px]">目标值{{ convertNumber(gainData.goalTotal || 0)}}</span>
<SvgComponent :content="powerSvg" class="w-[55px] h-[121px] z-1 my-[4px]" />
<span class="text-[#769CBF] text-[12px]">当前值0.5W</span>
<span class="text-[#769CBF] text-[12px]">当前值{{convertNumber(gainData.acqTotal ||0) }}</span>
</div>
</div>
</div>
@ -23,6 +23,7 @@
<script lang="ts" setup>
import SvgComponent from "@/components/SvgComponent.vue";
import {convertNumber} from "@/utils/convertNumber"
const groupSvg = ref("");
const getGroupBackgroundSvg = async () => {
@ -63,9 +64,13 @@
}
};
watch(powerFlag, () => {
const gainData = inject("gainData",ref({acqTotal:0,goalTotal:0}))
watch([() => powerFlag.value,()=>gainData.value], () => {
nextTick(() => {
setChargePercentageByPath(80);
if(gainData.value.goalTotal){
setChargePercentageByPath(gainData.value.acqTotal/gainData.value.goalTotal*100);
}
});
});

View File

@ -5,8 +5,8 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日流失</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10">
<span class="text-[34px] italic">125</span>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{ gainData.toDayOutFlow || 0 }}</span>
<span class="text-[18px]"></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
@ -17,12 +17,8 @@
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
<div class="w-[127px] h-[104px] top-0 left-0 z-1 absolute pl-[14px] flex flex-col justify-center">
<p class="text-[#C0EEFF] text-[12px]">总流失人数</p>
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177</p>
<div class="text-[#FF4E4E] text-[12px] flex items-center">
<span class="">今日较昨日</span>
<SvgIcon name="arrow-up" class="text-[9px]" />
<span>12</span>
</div>
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{ gainData.outFlowTotal || 0 }}</p>
</div>
</div>
</div>
@ -31,9 +27,9 @@
<script lang="ts" setup>
import SvgComponent from "@/components/SvgComponent.vue";
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
const gainData = inject('gainData',ref({toDayOutFlow:0,outFlowTotal:0}))
const groupSvg = ref("");
const getGroupBackgroundSvg = async () => {

View File

@ -17,11 +17,8 @@
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
</div>
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
<div
class=" bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px] ">
690
</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px]">690</div>
<div class="text-[20px]"></div>
</div>
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
@ -41,9 +38,9 @@
</ul>
</div>
</div>
</template>
</template>
<script setup lang="ts">
<script setup lang="ts">
import SvgComponent from "@/components/SvgComponent.vue";
import ProportionCharts from "@/views/components/chartsComponents/ProportionCharts.vue";
@ -71,13 +68,24 @@
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
const chartData = ref([
{ name: "类型A", value: 250, color: "#0783FA" },
{ name: "类型B", value: 274, color: "#07D1FA" },
{ name: "类型C", value: 310, color: "#20E6A4" },
{ name: "类型D", value: 135, color: "#FFD15C" },
{ name: "其他", value: 135, color: "#07D1FA" },
]);
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
const askSectionData = inject("askSectionData", ref<{ offline: any[] }>({ offline: [] }));
const chartData = ref<any[]>([]);
watch(
() => askSectionData.value,
() => {
if (askSectionData.value.offline.length > 0) {
chartData.value = askSectionData.value.offline.map((item, index) => ({
name: item.tag,
value: item.total,
color: colorList.value[index % colorList.value.length],
}));
}
},
);
onBeforeMount(() => {
getHeaderLeftSvg();
@ -85,9 +93,9 @@
getArrowLeftSvg();
getPaymentChartSvg();
});
</script>
</script>
<style scoped lang="scss">
<style scoped lang="scss">
@use "@/styles/custom-border.scss";
.custom-border {
@ -98,5 +106,4 @@
border-image-source: url("src/assets/svg-img/border-image.png");
border-style: solid;
}
</style>
</style>

View File

@ -17,11 +17,8 @@
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
</div>
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
<div
class=" bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px] ">
690
</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
<div class="text-[25px]">690</div>
<div class="text-[20px]"></div>
</div>
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线上</div>
@ -36,7 +33,7 @@
<span>{{ item.value }}</span>
</div>
</div>
<div class="border-image w-full m-[6px]"></div>
<div class="border-image w-full mt-[6px]"></div>
</li>
</ul>
</div>
@ -71,12 +68,22 @@
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
arrowLeftSvg.value = svg;
};
const chartData = ref([
{ name: "类型A", value: 250, color: "#0783FA" },
{ name: "类型B", value: 274, color: "#07D1FA" },
{ name: "类型C", value: 310, color: "#20E6A4" },
{ name: "类型D", value: 135, color: "#FFD15C" },
]);
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
const askSectionData = inject("askSectionData", ref<{ online: any[] }>({ online: [] }));
const chartData = ref<any[]>([]);
watch(() => askSectionData.value, () => {
if (askSectionData.value.online.length > 0) {
chartData.value = askSectionData.value.online.map((item, index) => ({
name: item.tag,
value: item.total,
color: colorList.value[index % colorList.value.length],
}));
}
});
onBeforeMount(() => {
getHeaderLeftSvg();

View File

@ -11,14 +11,36 @@
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
</div>
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
<OperatingTrendsChart />
<OperatingTrendsChart :chartDataArray="acqTrend" @date-change="handleDateChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { getRequest } from "@/api/customFetch";
import { getAcqTrend } from "@/api/fetchUrl";
import SvgComponent from "@/components/SvgComponent.vue";
import OperatingTrendsChart from "@/views/components/chartsComponents/OperatingTrendsChart.vue";
import { addRequest,removeRequest,runImmediatelyByKey } from "@/composables/usePolling";
let DateType = 0;
const acqTrend = ref<any>([]);
const getAcqTrendData = (type: number) => {
getRequest(getAcqTrend(), { Type: type }, {}).then((resp) => {
if (resp.code === 200) {
acqTrend.value = resp.result;
}
});
};
addRequest("getAcqTrendData", getAcqTrendData, [DateType]);
const handleDateChange = (type: string) => {
DateType = type === "week" ? 0 : 1;
removeRequest("getAcqTrendData"),
addRequest("getAcqTrendData", getAcqTrendData, [DateType]);
runImmediatelyByKey("getAcqTrendData");
};
const headerLeftSvg = ref("");
const headerRightSvg = ref("");

View File

@ -5,25 +5,25 @@
<div class="w-[216px]">
<div class="relative w-[154px] h-[124px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">总缴费</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10">
<span class="text-[40px] italic">1265</span>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[40px] italic">{{paymentData.chargeTotal}}</span>
<span class="text-[20px]"></span>
</div>
<SvgComponent :content="lightningBoxSvg" class="box-light absolute" />
</div>
<div>
<div class="flex items-center justify-between text-[12px]">
<div class="text-[#6B89BC]">当前值1265</div>
<div class="text-[#6B89BC]">当前值{{paymentData.chargeTotal}}</div>
<div class="text-[#69D4FF]">
计划:
<span class="text-[15px]">4000</span>
<span class="text-[15px]">{{paymentData.estimatedTotal}}</span>
<span class="text-[13px]"></span>
</div>
</div>
</div>
<YProgress :percentage="25" height="12px" class="mt-[7px]" />
<YProgress :percentage="Math.round(paymentData.chargeTotal/paymentData.estimatedTotal * 100)" height="12px" class="mt-[7px]" />
</div>
<div class="ml-[22px] relative">
<div class="ml-[4px] relative">
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-0 z-1" />
<div
@ -38,7 +38,7 @@
<div class="w-[6px] h-[6px] rounded-full" :style="{ backgroundColor: item.color }"></div>
<div class="flex-1 flex items-center text-[#C0EEFF] text-[12px] justify-between">
<span class="ml-[4px] mr-[9px]">{{ item.name }}</span>
<span>{{ item.value }}</span>
<span class="w-max break-keep">{{ item.value }}</span>
</div>
<div class="border-image w-full mt-[6px]"></div>
</li>
@ -58,12 +58,19 @@
groupSvg.value = svg;
};
const chartData = ref([
{ name: "类型A", value: 250, color: "#0783FA" },
{ name: "类型B", value: 274, color: "#07D1FA" },
{ name: "类型C", value: 310, color: "#20E6A4" },
{ name: "类型D", value: 135, color: "#FFD15C" },
]);
const paymentData = inject("paymentData",ref({chargeTotal:0,estimatedTotal:0,items:[]}))
const colors = ["#0783FA","#07D1FA","#20E6A4","#FFD15C","#9A68FF"]
const chartData = ref<any>([]);
watchEffect(() => {
if(paymentData.value.items){
chartData.value = paymentData.value.items.map((item:any,index) => {
return {...item,color:colors[index % colors.length]}
})
}
})
const lightningBoxSvg = ref("");
const getLightningBoxSvg = async () => {

View File

@ -17,11 +17,11 @@
</div>
<div class="flex items-baseline leading-[1] mt-[29px] mx-[27px]">
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[40px] font-700 italic pr-[4px]">189</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[40px] font-700 italic pr-[4px]">{{ sixStatisticsData.total || 0 }}</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[18px] font-700"></div>
<SvgIcon name="arrow-up" class="text-[9px] text-[#4AFFA2] ml-[9px]" />
</div>
<SixStatisticsChart />
<SixStatisticsChart :chartData="sixStatisticsData.items ||[]"/>
</div>
</div>
</template>
@ -56,6 +56,8 @@
moreArrowSvg.value = svg;
};
const sixStatisticsData = inject("sixStatisticsData",ref<{total:number,items:{data:string,total:number}[]}>({total:0,items:[]}))
onBeforeMount(() => {
getHeaderLeftSvg();
getHeaderRightSvg();

View File

@ -14,8 +14,8 @@
<StudentSourceChart
class="w-full h-full"
:chartData="[
{ name: '线下', value: 240, itemStyle: { color: 'rgba(147, 219, 255, 1)' } },
{ name: '线上', value: 600, itemStyle: { color: 'rgb(79, 214, 169)' } },
{ name: '线下', value: offlineTotal, itemStyle: { color: 'rgba(147, 219, 255, 1)' } },
{ name: '线上', value: onlineTotal, itemStyle: { color: 'rgb(79, 214, 169)' } },
]"
:ringSize="0.8" />
<div class="flex items-center justify-between mx-[20px] my-[33px]">
@ -23,7 +23,7 @@
<SvgComponent :content="onlineSvg" class="w-[53px] h-[38px]" />
<div class="flex flex-col ml-[9px] items-center">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent font-700">
<span class="text-[18px]">240</span>
<span class="text-[18px]">{{onlineTotal }}</span>
<span class="text-[14px]"></span>
</div>
<span class="text-[#C7F0FF] text-[12px]">线上来源</span>
@ -33,7 +33,7 @@
<SvgComponent :content="offlineSvg" class="w-[53px] h-[38px]" />
<div class="flex flex-col ml-[9px] items-center">
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent font-700">
<span class="text-[18px]">240</span>
<span class="text-[18px]">{{ offlineTotal }}</span>
<span class="text-[14px]"></span>
</div>
<span class="text-[#C7F0FF] text-[12px]">线下来源</span>
@ -78,6 +78,19 @@
onlineSvg.value = svg
}
const askSectionData = inject("askSectionData",ref<{online:any[],offline:any[]}>({online:[],offline:[]}))
const onlineTotal = ref(0)
const offlineTotal = ref(0)
watch(() =>askSectionData.value,() => {
if(askSectionData.value.online.length > 0){
onlineTotal.value = askSectionData.value.online.reduce((acc, curr) => acc + curr.total, 0)
}
if(askSectionData.value.offline.length > 0){
offlineTotal.value = askSectionData.value.offline.reduce((acc, curr) => acc + curr.total, 0)
}
})
onBeforeMount(() => {
getHeaderLeftSvg();

View File

@ -5,8 +5,8 @@
<div class="w-[144px]">
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
<div class="text-[#44C1EF] italic text-[20px] font-700">今日缴费</div>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10">
<span class="text-[34px] italic">125</span>
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent mb-[15px] z-10 font-700">
<span class="text-[34px] italic">{{paymentData.toDayTotal || 0}}</span>
<span class="text-[18px]"></span>
</div>
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
@ -17,11 +17,12 @@
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
<div class="w-[127px] h-[104px] top-0 left-0 z-1 absolute pl-[14px] flex flex-col justify-center">
<p class="text-[#C0EEFF] text-[12px]">昨日缴费人数</p>
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177</p>
<div class="text-[#FF4E4E] text-[12px] flex items-center">
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{paymentData.yesterdayTotal || 0}}</p>
<div :class="`${paymentDifferent > 0 ?'text-[#4AFFA2]': 'text-[#FF4E4E]'} text-[12px] flex items-center`">
<span class="">今日较昨日</span>
<SvgIcon name="arrow-up" class="text-[9px]" />
<span>12</span>
<SvgIcon :name="paymentDifferent > 0 ? 'arrow-up' : 'arrow-down'" class="text-[9px]" />
<span>{{Math.abs(paymentDifferent)}}</span>
</div>
</div>
</div>
@ -33,6 +34,7 @@
import SvgComponent from "@/components/SvgComponent.vue";
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
const paymentData = inject("paymentData",ref({toDayTotal:0,yesterdayTotal:0}))
const groupSvg = ref("");
@ -55,6 +57,14 @@
paymentBorderSvg.value = svg;
};
const paymentDifferent = computed(() => {
if(Object.prototype.toString.call(paymentData.value.toDayTotal) !== "[object Undefined]" && Object.prototype.toString.call(paymentData.value.yesterdayTotal)!== "[object Undefined]"){
return paymentData.value.toDayTotal - paymentData.value.yesterdayTotal
}else{
return 0
}
})
onBeforeMount(() => {
getGroupBackgroundSvg();
getLightningBoxSvg();

View File

@ -41,10 +41,27 @@
const columns = [
{ field: "rank", header: "名次", align: "justify-center",width:'68px' },
{ field: "name", header: "姓名", align: "justify-left",width:'100px' },
{ field: "Status", header: "获客人数", align: "justify-center",width:'96px' },
{ field: "total", header: "获客人数", align: "justify-center",width:'96px' },
];
const products = [{ name: "张三" },{ name: "张三" },{ name: "张三" },{ name: "张三" },{ name: "张三" }];
let products = ref<any[]>([]);
const chargingRankingData = inject(
"chargingRankingData",
ref<{
acqRanks: any[];
}>({
acqRanks: [],
}),
);
watch(
() => chargingRankingData.value,
() => {PageTransitionEvent
if(chargingRankingData.value.acqRanks){
products.value = chargingRankingData.value.acqRanks
}
},
);
const headerLeftSvg = ref("");
const headerRightSvg = ref("");

View File

@ -4,7 +4,16 @@
<script setup lang="ts">
import * as echarts from "echarts";
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, defineProps, watch } from "vue";
interface DataItem {
tag: string;
total: number;
}
const props = defineProps<{
chartData: DataItem[];
}>();
const renderItem = (params: any, api: { coord: (arg0: any[]) => any[]; value: (arg0: number) => any; style: () => any }) => {
//
@ -85,7 +94,8 @@
let myChart: echarts.ECharts | null = null;
const initChart = () => {
if (chartDom.value) {
if (!chartDom.value) return;
myChart = echarts.init(chartDom.value);
//
const WIDTH = 10;
@ -159,6 +169,7 @@
echarts.graphic.registerShape("topShape", topShape);
echarts.graphic.registerShape("middleShape", middleShape);
// updateChartData
const options = {
grid: {
top: "12%",
@ -168,11 +179,19 @@
},
xAxis: {
type: "category",
data: [1, 2, 3, 4, 5],
data: [],
axisLabel: {
formatter: (value: any) => value,
formatter: (value: any) => {
if (value.length <= 4) return value;
const arr = [];
for (let i = 0; i < value.length; i += 4) {
arr.push(value.slice(i, i + 4));
}
return arr.join('\n');
},
color: "#C0EEFF",
fontSize: 14,
interval: 0,
},
nameTextStyle: {
color: "#243174",
@ -186,7 +205,6 @@
yAxis: {
name: "人数",
nameLocation: "end",
data: [100, 200, 300, 400],
nameTextStyle: {
color: "#C0EEFF",
fontSize: 14,
@ -195,8 +213,8 @@
},
type: "value",
min: 0,
max: 500,
interval: 100,
max: 100,
interval: 20,
axisLabel: {
color: "#C0EEFF",
fontSize: 14,
@ -221,7 +239,7 @@
type: "custom",
renderItem: renderItem,
color: "blue",
data: [100, 200, 300, 400],
data: [],
},
{
type: "bar",
@ -237,21 +255,71 @@
itemStyle: {
color: "transparent",
},
data: [100, 200, 300, 400],
data: [],
},
],
};
myChart.setOption(options);
window.addEventListener("resize", handleResize);
//
if (props.chartData && props.chartData.length > 0) {
updateChartData();
}
window.addEventListener("resize", handleResize);
};
//
const updateChartData = () => {
if (!myChart || !props.chartData) return;
const chartData = props.chartData || [];
//
const tags = chartData.map(item => item.tag);
const values = chartData.map(item => item.total);
// y
const maxValue = values.length > 0 ? Math.max(...values) : 0;
const yAxisMax = maxValue > 0 ? Math.ceil(maxValue / 100) * 100 : 100;
const yAxisInterval = Math.ceil(yAxisMax / 5 / 100) * 100 || 20;
myChart.setOption({
xAxis: {
data: tags,
},
yAxis: {
min: 0,
max: yAxisMax,
interval: yAxisInterval,
},
series: [
{
data: values,
},
{
data: values,
},
],
});
};
const handleResize = () => {
myChart?.resize();
};
//
watch(
() => props.chartData,
() => {
if (myChart) {
updateChartData();
}
},
{ deep: true }
);
onMounted(() => {
initChart();
});

View File

@ -3,20 +3,136 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { onMounted, ref, watch, computed } from "vue";
import * as echarts from "echarts";
const chartDom = ref(null);
let myChart: echarts.ECharts | null = null;
//
const chartData = ref({
const props = defineProps({
chartDataArray: {
type: Array,
default: () => []
},
});
const emits = defineEmits(["dateChange"]);
//
interface ChartDataItem {
date: string;
value: number;
}
interface ChartTypeData {
type: number;
items: ChartDataItem[];
}
interface SeriesDataItem {
name: string;
data: number[];
color: string;
}
interface ChartDataFormat {
dates: string[];
seriesData: SeriesDataItem[];
}
//
const processChartData = (dataArray: ChartTypeData[], mode: string): ChartDataFormat => {
if (!dataArray || dataArray.length === 0) return {
dates: [],
seriesData: [
{ name: "获客", data: [], color: "#20E6A4" },
{ name: "缴费", data: [], color: "#0783FA" },
{ name: "流失", data: [], color: "#FFD15C" },
],
};
//
let allDates = new Set<string>();
dataArray.forEach((item: ChartTypeData) => {
if (item.items && item.items.length > 0) {
item.items.forEach((dateItem: ChartDataItem) => {
if (dateItem.date) {
allDates.add(dateItem.date);
}
});
}
});
//
let sortedDates = Array.from(allDates).sort((a: string, b: string) => {
// MM-DD
const [monthA, dayA] = a.split('-').map(Number);
const [monthB, dayB] = b.split('-').map(Number);
if (monthA !== monthB) return monthA - monthB;
return dayA - dayB;
});
//
if (mode === "week") {
// 7
if (sortedDates.length > 7) {
sortedDates = sortedDates.slice(-7);
}
}
// 30
else if (mode === "month") {
// 3030
if (sortedDates.length > 30) {
sortedDates = sortedDates.slice(-30);
}
//
}
//
const formattedDates = sortedDates.map((date: string) => {
const [month, day] = date.split('-');
return `${month}.${day}`;
});
//
const seriesData: SeriesDataItem[] = [
{ name: "获客", data: [], color: "#20E6A4" },
{ name: "缴费", data: [], color: "#0783FA" },
{ name: "流失", data: [], color: "#FFD15C" },
];
//
sortedDates.forEach((date: string) => {
//
dataArray.forEach((typeData: ChartTypeData, typeIndex: number) => {
if (typeIndex < seriesData.length) {
const dateItem = typeData.items.find(item => item.date === date);
seriesData[typeIndex].data.push(dateItem ? dateItem.value : 0);
}
});
});
return {
dates: formattedDates,
seriesData,
};
};
// 使computedprops
const chartData = computed((): ChartDataFormat => {
if (props.chartDataArray && props.chartDataArray.length > 0) {
return processChartData(props.chartDataArray as ChartTypeData[], currentMode.value);
}
// 使
return {
dates: ["5.24", "5.25", "5.26", "5.27", "5.28", "5.29", "5.30"],
seriesData: [
{ name: "获客", data: [180, 230, 200, 280, 250, 300, 350], color: "#20E6A4" },
{ name: "缴费", data: [150, 210, 180, 240, 200, 260, 320], color: "#0783FA" },
{ name: "流失", data: [120, 380, 220, 350, 300, 280, 400], color: "#FFD15C" },
],
};
});
const currentMode = ref("week");
@ -70,7 +186,7 @@
//
const generateSeries = (seriesData: any) => {
return seriesData.map((item: any,) => {
return seriesData.map((item: any) => {
const series = {
name: item.name,
type: "line",
@ -94,16 +210,7 @@
// /
const switchDisplayMode = (mode: string) => {
currentMode.value = mode;
//
if (mode === "week") {
// ()
chartData.value.dates = ["5.24", "5.25", "5.26", "5.27", "5.28", "5.29", "5.30"];
} else {
//
chartData.value.dates = ["1月", "2月", "3月", "4月", "5月", "6月", "7月"];
}
emits("dateChange", mode);
//
initChart();
};
@ -352,17 +459,16 @@
color: "transparent",
},
tooltip: {
show: false
}
}
show: false,
},
},
],
};
myChart.setOption(option, true);
//
const hoverBarId = 'mouseHoverBar';
const hoverBarId = "mouseHoverBar";
myChart.on("mouseover", { seriesId: "axisOverlayBar" }, (params) => {
if (!myChart) return;
@ -386,18 +492,18 @@
//
const hoverBarOption = {
id: hoverBarId,
type: 'bar',
type: "bar",
barWidth: 40,
barGap: '-100%',
barGap: "-100%",
zlevel: 10,
silent: true,
animation: true,
data: chartData.value.dates.map((_: any, index: number) => {
return index === dataIndex ? 500 : '-'
return index === dataIndex ? 500 : "-";
}),
itemStyle: {
color: {
type: 'linear',
type: "linear",
x: 0,
y: 0,
x2: 0,
@ -405,20 +511,20 @@
colorStops: [
{
offset: 0,
color: '#fff' //
color: "#fff", //
},
{
offset: 1,
color: '#0783FA' //
}
color: "#0783FA", //
},
],
global: false
global: false,
},
opacity: 0.3,
},
tooltip: {
show: false
}
show: false,
},
};
if (seriesIndex === -1) {
@ -439,7 +545,6 @@
//
myChart.on("mouseout", { seriesId: "axisOverlayBar" }, () => {
if (!myChart) return;
const _option = myChart.getOption() as any;
const hoverBarExists = _option.series.find((series: any) => series.id && series.id === hoverBarId);
@ -471,9 +576,13 @@
};
onMounted(() => {
initChart();
window.addEventListener("resize", () => myChart?.resize());
});
//
watch(() => props.chartDataArray, () => {
initChart();
}, { deep: true });
</script>
<style lang="scss" scoped>

View File

@ -5,121 +5,139 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import { ref, onMounted, onUnmounted } from "vue";
import * as echarts from "echarts";
//
const chartRef = ref<HTMLElement | null>(null)
let chartInstance: echarts.ECharts | null = null
//
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
// prop
const props = defineProps({
// prop
const props = defineProps({
chartData: {
type: Array,
required: true,
default: () => []
}
})
default: () => [],
},
});
//
const initChart = () => {
if (chartRef.value) {
//
if (chartInstance) {
chartInstance.dispose()
}
//
chartInstance = echarts.init(chartRef.value)
//
const generatedFormattedData = () => {
const formattedData = props.chartData.map((item: any) => ({
...item,
// itemStylecolor
itemStyle: {
color: item.color || (item.itemStyle && item.itemStyle.color),
borderWidth: 0 //
borderWidth: 0, //
},
}));
return formattedData;
};
//
const initChart = () => {
if (chartRef.value) {
//
if (chartInstance) {
chartInstance.dispose();
}
}))
//
chartInstance = echarts.init(chartRef.value);
//
//
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#44F1FF',
trigger: "item",
backgroundColor: "rgba(0, 0, 0, 0.7)",
borderColor: "#44F1FF",
textStyle: {
color: '#fff',
fontSize: 10
color: "#fff",
fontSize: 10,
},
formatter: '{b}: {d}%',
formatter: "{b}: {d}%",
},
series: [
{
name: '数据占比',
type: 'pie',
radius: ['70%', '90%'],
center: ['50%', '50%'],
name: "数据占比",
type: "pie",
radius: ["70%", "90%"],
center: ["50%", "50%"],
avoidLabelOverlap: false,
itemStyle: {
borderWidth: 0 //
borderWidth: 0, //
},
label: {
show: false
show: false,
},
emphasis: {
scale: false,
itemStyle: {
shadowBlur: 5,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
labelLine: {
show: false
show: false,
},
data: formattedData,
data: generatedFormattedData(),
//
gap: 0
}
gap: 0,
},
],
animation: true,
animationDuration: 500
}
animationDuration: 500,
};
//
chartInstance.setOption(option)
chartInstance.setOption(option);
//
window.addEventListener('resize', handleResize)
window.addEventListener("resize", handleResize);
}
}
};
//
const handleResize = () => {
//
const handleResize = () => {
if (chartInstance) {
chartInstance.resize()
chartInstance.resize();
}
}
};
//
onMounted(() => {
initChart()
})
//
onMounted(() => {
initChart();
});
onUnmounted(() => {
watch(
() => props.chartData,
() => {
if (chartInstance) {
chartInstance.setOption({
series: [
{
data: generatedFormattedData(),
},
],
});
}
},
{ deep: true },
);
onUnmounted(() => {
//
window.removeEventListener('resize', handleResize)
window.removeEventListener("resize", handleResize);
//
if (chartInstance) {
chartInstance.dispose()
chartInstance = null
chartInstance.dispose();
chartInstance = null;
}
})
});
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

View File

@ -25,6 +25,17 @@ echarts.use([
const chartRef = useTemplateRef("chartRef")
let chart: echarts.ECharts | null = null;
const props = defineProps({
chartData:{
type:Array,
default: () => []
}
})
watch(() => props.chartData,() => {
initChart();
})
const initChart = () => {
if(!chartRef.value) return;
chart = echarts.init(chartRef.value)
@ -42,7 +53,7 @@ const initChart = () => {
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
data: props.chartData.map((item:any) => item.date),
axisLine: {
show: false
},
@ -73,7 +84,7 @@ const initChart = () => {
name: '数据1',
type: 'line',
smooth: true,
data: [120, 132, 101, 134, 90, 230, 210],
data: props.chartData.map((item:any) => item.total),
lineStyle: {
color: 'rgba(41, 241, 250, 1)',
width: 2
@ -103,7 +114,7 @@ const initChart = () => {
}
onMounted(() => {
initChart();
window.addEventListener('resize', () => {
chart?.resize();
});

View File

@ -28,8 +28,8 @@
const props = withDefaults(defineProps<DefaultProps>(), {
chartData: [
{ name: "线下", value: 310, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
{ name: "线上", value: 335, itemStyle: { color: "rgb(79, 214, 169)" } },
{ name: "线下", value: 0, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
{ name: "线上", value: 0, itemStyle: { color: "rgb(79, 214, 169)" } },
],
ringSize: 1,
});
@ -206,6 +206,7 @@
height: 106,
},
z: 0,
cursor:"point",
origin: [127, 53],
keyframeAnimation: [
{

View File

@ -0,0 +1,65 @@
import {getRequest} from "@/api/customFetch";
import { getBigScreenRanking, getGainUrl, getPaymentUrl, getSegmentStatic, getSixStatisticsUrl } from "@/api/fetchUrl";
import { usePolling,addRequest } from "@/composables/usePolling";
export const useFetchAllData = () => {
const paymentData = ref<any>({})
const getPaymentData = () => {
getRequest(getPaymentUrl(),{},{}).then(resp => {
if(resp.code == 200){
paymentData.value = JSON.parse(JSON.stringify(resp.result))
}
})
};
addRequest('paymentData',getPaymentData)
provide("paymentData",paymentData)
const gainData = ref<any>({})
const getGainData = () => {
getRequest(getGainUrl(),null,{}).then(resp => {
if(resp.code === 200){
gainData.value = resp.result
}
})
}
addRequest("getGainData",getGainData)
provide("gainData",gainData)
const askSectionData = ref<any>({})
const getAskSectionData = () => {
getRequest(getSegmentStatic(), {}, {}).then((resp) => {
if (resp.code === 200) {
askSectionData.value = resp.result;
}
});
};
addRequest("getAskSectionData",getAskSectionData)
provide("askSectionData",askSectionData)
const chargingRankingData = ref<any>({})
const getChargingRankingData = () => {
getRequest(getBigScreenRanking(),{},{}).then(resp => {
if(resp.code === 200){
chargingRankingData.value = resp.result
}
})
}
addRequest("getChargingRankingData",getChargingRankingData)
provide("chargingRankingData",chargingRankingData)
const sixStatisticsData = ref<any>({})
const getSixStaticsData = () => {
getRequest(getSixStatisticsUrl(),{},{}).then(resp => {
if(resp.code === 200){
sixStatisticsData.value = resp.result
}
})
}
addRequest('getSixStaticsData',getSixStaticsData)
provide('sixStatisticsData',sixStatisticsData)
usePolling();
}

View File

@ -1,5 +1,5 @@
<template>
<div class="main-bg flex flex-col scrollbar-hide h-screen overflow-auto ">
<div class="main-bg flex flex-col scrollbar-hide h-screen overflow-auto">
<header class="relative flex items-center">
<SvgComponent :content="headerSvg" class="w-full h-[98px]" />
<SvgComponent :content="titleSvg" class="w-[50%] h-[69px] absolute top-0 left-50% translate-x-[-50%]" />
@ -8,9 +8,9 @@
<DigitalWatch class="ml-[10px]" />
</div>
</header>
<div class="flex items-center justify-end pr-[24px] cursor-pointer mb-[13px]">
<SvgIcon name="circle" class="text-[14px] text-[#C0EEFF] hover:rotate-90 transition-all duration-300" />
<div class="text-[#C0EEFF] text-[12px] ml-[5px]">数据更新时间:6.12 12:00:00</div>
<div class="flex items-center justify-end pr-[24px] cursor-pointer mb-[13px]" @click="updateAllData">
<SvgIcon name="circle" class="text-[14px] text-[#C0EEFF] transition-all duration-300"/>
<div class="text-[#C0EEFF] text-[12px] ml-[5px]">数据更新时间:{{ formatDatetime(updateTime) }}</div>
</div>
<div class="grid grid-rows-[210px_358px_320px] gap-y-5 w-full min-h-[928px] gap-[20px] flex-1 overflow-auto mb-[27px]">
@ -22,14 +22,14 @@
<LossStatic class="ml-[20px]" />
</div>
<div class="flex items-center px-[24px] justify-start">
<OperatingTrends class=""/>
<AskSection class="ml-[20px]"/>
<OperatingTrends class="" />
<AskSection class="ml-[20px]" />
</div>
<div class="flex items-center px-[24px] overflow-x-auto">
<StudentSource />
<OnLineStatus class="ml-[20px]"/>
<OnLineStatus class="ml-[20px]" />
<OfflineStatus class="ml-[20px]" />
<SixStatistics class="ml-[20px]"/>
<SixStatistics class="ml-[20px]" />
<ChargingRanking class="ml-[20px]" />
<WinCustomer class="ml-[20px]" />
</div>
@ -57,9 +57,12 @@
import WinCustomer from "./components/WinCustomer.vue";
import { useDate } from "@/composables/useDate";
import {useFetchAllData} from "./composables/useFetchData"
import { runImmediatelyAll, updateTime } from "@/composables/usePolling";
import { formatDatetime } from "@/utils/date";
const { year, month, day, weekday, formateTime } = useDate();
const { year, month, day, weekday, updateTime } = useDate();
const headerSvg = ref("");
const headerBackgroundSvg = async () => {
@ -74,14 +77,17 @@
};
useFetchAllData()
const updateAllData = () => {
runImmediatelyAll()
}
onBeforeMount(() => {
updateTime();
formateTime();
headerBackgroundSvg();
headerTitleSvg();
});
</script>
<style scoped lang="scss">
@ -92,5 +98,4 @@
background-repeat: no-repeat;
overflow-y: auto;
}
</style>