feat: 接口对接
parent
2254df4e95
commit
eba5899cd6
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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");
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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`
|
||||||
|
|
@ -117,11 +117,11 @@ const getSegmentClass = (digit: string, index: number) => {
|
||||||
|
|
||||||
let timer: number | null = null;
|
let timer: number | null = null;
|
||||||
|
|
||||||
let { hours, minutes, seconds, updateTime } = useDate()
|
let { hours, minutes, seconds, formateTime } = useDate()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateTime();
|
formateTime();
|
||||||
timer = window.setInterval(updateTime, 500);
|
timer = window.setInterval(formateTime, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export const useDate = () => {
|
||||||
|
|
||||||
let timer: number | null = null;
|
let timer: number | null = null;
|
||||||
|
|
||||||
const updateTime = () => {
|
const formateTime = () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
hours.value = now.getHours().toString().padStart(2, "0");
|
hours.value = now.getHours().toString().padStart(2, "0");
|
||||||
|
|
@ -35,6 +35,6 @@ export const useDate = () => {
|
||||||
day,
|
day,
|
||||||
weekday, // 返回星期几
|
weekday, // 返回星期几
|
||||||
timer,
|
timer,
|
||||||
updateTime,
|
formateTime,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -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();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
};
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
|
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
|
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
|
||||||
<AskSectionChart />
|
<AskSectionChart :chart-data="askSectionData.stages || []" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -38,7 +38,10 @@
|
||||||
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
||||||
arrowLeftSvg.value = svg;
|
arrowLeftSvg.value = svg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const askSectionData = inject("askSectionData",ref({stages:[]}))
|
||||||
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getHeaderLeftSvg();
|
getHeaderLeftSvg();
|
||||||
getHeaderRightSvg();
|
getHeaderRightSvg();
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,28 @@
|
||||||
const columns = [
|
const columns = [
|
||||||
{ field: "rank", header: "名次", align: "justify-center", width: "68px" },
|
{ field: "rank", header: "名次", align: "justify-center", width: "68px" },
|
||||||
{ field: "name", header: "姓名", align: "justify-left", width: "100px" },
|
{ 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 headerLeftSvg = ref("");
|
||||||
const headerRightSvg = ref("");
|
const headerRightSvg = ref("");
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<div class="w-[144px]">
|
<div class="w-[144px]">
|
||||||
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">今日获客</div>
|
<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">
|
<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">125</span>
|
<span class="text-[34px] italic">{{ gainData.toDayAcq || 0 }}</span>
|
||||||
<span class="text-[18px]">人</span>
|
<span class="text-[18px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
|
<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">
|
<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-[12px]">昨日获客人数</p>
|
||||||
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177人</p>
|
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{ gainData.yestertDayAcq || 0 }}人</p>
|
||||||
<div class="text-[#FF4E4E] text-[12px] flex items-center">
|
<div :class="`${gainDifferentTody > 0 ?'text-[#4AFFA2]':'text-[#FF4E4E]'} text-[12px] flex items-center`">
|
||||||
<span class="">今日较昨日</span>
|
<span class="">今日较昨日</span>
|
||||||
<SvgIcon name="arrow-up" class="text-[9px]" />
|
<SvgIcon :name="gainDifferentTody > 0 ? 'arrow-up' : 'arrow-down'" class="text-[9px]" />
|
||||||
<span>12人</span>
|
<span>{{ Math.abs(gainDifferentTody) }}人</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
|
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const gainData = inject("gainData",ref({toDayAcq:0,yestertDayAcq:0}))
|
||||||
|
|
||||||
const groupSvg = ref("");
|
const groupSvg = ref("");
|
||||||
const getGroupBackgroundSvg = async () => {
|
const getGroupBackgroundSvg = async () => {
|
||||||
|
|
@ -41,6 +42,13 @@
|
||||||
groupSvg.value = svg;
|
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("");
|
const hexagonalBoxSvg = ref("");
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,17 @@
|
||||||
<div class="w-[144px]">
|
<div class="w-[144px]">
|
||||||
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
|
<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">
|
<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">125</span>
|
<span class="text-[34px] italic">{{gainData.acqTotal}}</span>
|
||||||
<span class="text-[18px]">人</span>
|
<span class="text-[18px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative ml-[37px] flex flex-col items-center">
|
<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]" />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import SvgComponent from "@/components/SvgComponent.vue";
|
import SvgComponent from "@/components/SvgComponent.vue";
|
||||||
|
import {convertNumber} from "@/utils/convertNumber"
|
||||||
|
|
||||||
const groupSvg = ref("");
|
const groupSvg = ref("");
|
||||||
const getGroupBackgroundSvg = async () => {
|
const getGroupBackgroundSvg = async () => {
|
||||||
|
|
@ -63,9 +64,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(powerFlag, () => {
|
const gainData = inject("gainData",ref({acqTotal:0,goalTotal:0}))
|
||||||
|
|
||||||
|
watch([() => powerFlag.value,()=>gainData.value], () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setChargePercentageByPath(80);
|
if(gainData.value.goalTotal){
|
||||||
|
setChargePercentageByPath(gainData.value.acqTotal/gainData.value.goalTotal*100);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<div class="w-[144px]">
|
<div class="w-[144px]">
|
||||||
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">今日流失</div>
|
<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">
|
<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">125</span>
|
<span class="text-[34px] italic">{{ gainData.toDayOutFlow || 0 }}</span>
|
||||||
<span class="text-[18px]">人</span>
|
<span class="text-[18px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
||||||
|
|
@ -17,12 +17,8 @@
|
||||||
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
|
<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">
|
<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-[12px]">总流失人数</p>
|
||||||
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177人</p>
|
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{ gainData.outFlowTotal || 0 }}人</p>
|
||||||
<div class="text-[#FF4E4E] text-[12px] flex items-center">
|
|
||||||
<span class="">今日较昨日</span>
|
|
||||||
<SvgIcon name="arrow-up" class="text-[9px]" />
|
|
||||||
<span>12人</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -31,9 +27,9 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import SvgComponent from "@/components/SvgComponent.vue";
|
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 groupSvg = ref("");
|
||||||
const getGroupBackgroundSvg = async () => {
|
const getGroupBackgroundSvg = async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,109 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-[296px] h-[320px] relative bg-[#082059]">
|
<div class="w-[296px] h-[320px] relative bg-[#082059]">
|
||||||
<div class="flex h-full custom-border absolute top-0 left-0">
|
<div class="flex h-full custom-border absolute top-0 left-0">
|
||||||
<div class="relative h-[36px]">
|
<div class="relative h-[36px]">
|
||||||
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
|
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
|
||||||
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
|
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
|
||||||
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">线下详情</div>
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">线下详情</div>
|
||||||
</div>
|
|
||||||
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
|
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
|
<SvgComponent :content="headerLeftSvg" class="w-[188px] h-[36px]" />
|
||||||
</div>
|
|
||||||
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
|
|
||||||
<div class="ml-[22px] relative mt-[13px]">
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
|
|
||||||
<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="text-[20px]">人</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ul class="px-[24px] grid grid-cols-2 gap-x-[20px] gap-y-[16px] mt-[20px] leading-[1]">
|
|
||||||
<li class="flex items-center flex-col" v-for="item in chartData" :key="item.name">
|
|
||||||
<div class="flex items-center justify-between w-full">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="border-image w-full mt-[6px]"></div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<SvgComponent :content="headerRightSvg" class="w-[108px] h-[36px]" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div class="w-full h-[calc(100%-36px)] mt-[36px] flex flex-col">
|
||||||
|
<div class="ml-[22px] relative mt-[13px]">
|
||||||
<script setup lang="ts">
|
<div class="flex items-center justify-center">
|
||||||
import SvgComponent from "@/components/SvgComponent.vue";
|
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
|
||||||
import ProportionCharts from "@/views/components/chartsComponents/ProportionCharts.vue";
|
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
|
||||||
|
</div>
|
||||||
const headerLeftSvg = ref("");
|
<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">
|
||||||
const headerRightSvg = ref("");
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
||||||
|
<div class="text-[25px]">690</div>
|
||||||
const getHeaderLeftSvg = async () => {
|
<div class="text-[20px]">人</div>
|
||||||
const { default: svg } = await import("/src/assets/svg-img/header-bg-left-sort.svg?raw");
|
</div>
|
||||||
headerLeftSvg.value = svg;
|
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
|
||||||
};
|
</div>
|
||||||
|
</div>
|
||||||
const getHeaderRightSvg = async () => {
|
<ul class="px-[24px] grid grid-cols-2 gap-x-[20px] gap-y-[16px] mt-[20px] leading-[1]">
|
||||||
const { default: svg } = await import("/src/assets/svg-img/header-bg-right-sort.svg?raw");
|
<li class="flex items-center flex-col" v-for="item in chartData" :key="item.name">
|
||||||
headerRightSvg.value = svg;
|
<div class="flex items-center justify-between w-full">
|
||||||
};
|
<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">
|
||||||
const paymentChartSvg = ref("");
|
<span class="ml-[4px] mr-[9px]">{{ item.name }}</span>
|
||||||
const getPaymentChartSvg = async () => {
|
<span>{{ item.value }}人</span>
|
||||||
const { default: svg } = await import("/src/assets/svg-img/payment-chart.svg?raw");
|
</div>
|
||||||
paymentChartSvg.value = svg;
|
</div>
|
||||||
};
|
<div class="border-image w-full mt-[6px]"></div>
|
||||||
|
</li>
|
||||||
const arrowLeftSvg = ref("");
|
</ul>
|
||||||
const getArrowLeftSvg = async () => {
|
</div>
|
||||||
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
</div>
|
||||||
arrowLeftSvg.value = svg;
|
</template>
|
||||||
};
|
|
||||||
const chartData = ref([
|
<script setup lang="ts">
|
||||||
{ name: "类型A", value: 250, color: "#0783FA" },
|
import SvgComponent from "@/components/SvgComponent.vue";
|
||||||
{ name: "类型B", value: 274, color: "#07D1FA" },
|
import ProportionCharts from "@/views/components/chartsComponents/ProportionCharts.vue";
|
||||||
{ name: "类型C", value: 310, color: "#20E6A4" },
|
|
||||||
{ name: "类型D", value: 135, color: "#FFD15C" },
|
const headerLeftSvg = ref("");
|
||||||
{ name: "其他", value: 135, color: "#07D1FA" },
|
const headerRightSvg = ref("");
|
||||||
]);
|
|
||||||
|
const getHeaderLeftSvg = async () => {
|
||||||
onBeforeMount(() => {
|
const { default: svg } = await import("/src/assets/svg-img/header-bg-left-sort.svg?raw");
|
||||||
getHeaderLeftSvg();
|
headerLeftSvg.value = svg;
|
||||||
getHeaderRightSvg();
|
};
|
||||||
getArrowLeftSvg();
|
|
||||||
getPaymentChartSvg();
|
const getHeaderRightSvg = async () => {
|
||||||
});
|
const { default: svg } = await import("/src/assets/svg-img/header-bg-right-sort.svg?raw");
|
||||||
</script>
|
headerRightSvg.value = svg;
|
||||||
|
};
|
||||||
<style scoped lang="scss">
|
|
||||||
@use "@/styles/custom-border.scss";
|
const paymentChartSvg = ref("");
|
||||||
|
const getPaymentChartSvg = async () => {
|
||||||
.custom-border {
|
const { default: svg } = await import("/src/assets/svg-img/payment-chart.svg?raw");
|
||||||
border-image-slice: 27 27 27 27;
|
paymentChartSvg.value = svg;
|
||||||
border-image-width: 1px 1px 2px 1px;
|
};
|
||||||
border-image-outset: 0px 0px 0px 0px;
|
|
||||||
border-image-repeat: stretch stretch;
|
const arrowLeftSvg = ref("");
|
||||||
border-image-source: url("src/assets/svg-img/border-image.png");
|
const getArrowLeftSvg = async () => {
|
||||||
border-style: solid;
|
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
||||||
}
|
arrowLeftSvg.value = svg;
|
||||||
</style>
|
};
|
||||||
|
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();
|
||||||
|
getHeaderRightSvg();
|
||||||
|
getArrowLeftSvg();
|
||||||
|
getPaymentChartSvg();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "@/styles/custom-border.scss";
|
||||||
|
|
||||||
|
.custom-border {
|
||||||
|
border-image-slice: 27 27 27 27;
|
||||||
|
border-image-width: 1px 1px 2px 1px;
|
||||||
|
border-image-outset: 0px 0px 0px 0px;
|
||||||
|
border-image-repeat: stretch stretch;
|
||||||
|
border-image-source: url("src/assets/svg-img/border-image.png");
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,8 @@
|
||||||
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
|
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-[50%] z-1 transform-translate-x-[-50%]" />
|
||||||
</div>
|
</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="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
|
||||||
<div
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
||||||
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-[25px] ">
|
|
||||||
690
|
|
||||||
</div>
|
|
||||||
<div class="text-[20px]">人</div>
|
<div class="text-[20px]">人</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线上</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>
|
<span>{{ item.value }}人</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-image w-full m-[6px]"></div>
|
<div class="border-image w-full mt-[6px]"></div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,12 +68,22 @@
|
||||||
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
const { default: svg } = await import("/src/assets/svg-img/arrow-left.svg?raw");
|
||||||
arrowLeftSvg.value = svg;
|
arrowLeftSvg.value = svg;
|
||||||
};
|
};
|
||||||
const chartData = ref([
|
|
||||||
{ name: "类型A", value: 250, color: "#0783FA" },
|
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
|
||||||
{ name: "类型B", value: 274, color: "#07D1FA" },
|
|
||||||
{ name: "类型C", value: 310, color: "#20E6A4" },
|
const askSectionData = inject("askSectionData", ref<{ online: any[] }>({ online: [] }));
|
||||||
{ name: "类型D", value: 135, color: "#FFD15C" },
|
|
||||||
]);
|
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(() => {
|
onBeforeMount(() => {
|
||||||
getHeaderLeftSvg();
|
getHeaderLeftSvg();
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-[926px] h-[358px] relative bg-[#082059]">
|
<div class="w-[926px] h-[358px] relative bg-[#082059]">
|
||||||
<div class="flex h-full custom-border absolute top-0 left-0">
|
<div class="flex h-full custom-border absolute top-0 left-0">
|
||||||
<div class="relative h-[36px]">
|
<div class="relative h-[36px]">
|
||||||
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
|
<div class="absolute top-[50%] translate-y-[-50%] left-[15px] flex items-center">
|
||||||
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
|
<SvgComponent :content="arrowLeftSvg" class="w-[15px] h-[18px]" />
|
||||||
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">经营趋势</div>
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent text-[20px] ml-[9px] font-700">经营趋势</div>
|
||||||
</div>
|
|
||||||
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<SvgComponent :content="headerLeftSvg" class="w-[255px] h-[36px]" />
|
||||||
|
</div>
|
||||||
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
|
<SvgComponent :content="headerRightSvg" class="w-[669px] h-[36px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
|
<div class="w-full h-[calc(100%-36px)] mt-[36px]">
|
||||||
<OperatingTrendsChart />
|
<OperatingTrendsChart :chartDataArray="acqTrend" @date-change="handleDateChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getAcqTrend } from "@/api/fetchUrl";
|
||||||
import SvgComponent from "@/components/SvgComponent.vue";
|
import SvgComponent from "@/components/SvgComponent.vue";
|
||||||
import OperatingTrendsChart from "@/views/components/chartsComponents/OperatingTrendsChart.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 headerLeftSvg = ref("");
|
||||||
const headerRightSvg = ref("");
|
const headerRightSvg = ref("");
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,25 @@
|
||||||
<div class="w-[216px]">
|
<div class="w-[216px]">
|
||||||
<div class="relative w-[154px] h-[124px] flex items-center flex-col">
|
<div class="relative w-[154px] h-[124px] flex items-center flex-col">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">总缴费</div>
|
<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">
|
<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">1265</span>
|
<span class="text-[40px] italic">{{paymentData.chargeTotal}}</span>
|
||||||
<span class="text-[20px]">人</span>
|
<span class="text-[20px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="lightningBoxSvg" class="box-light absolute" />
|
<SvgComponent :content="lightningBoxSvg" class="box-light absolute" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-between text-[12px]">
|
<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]">
|
<div class="text-[#69D4FF]">
|
||||||
计划:
|
计划:
|
||||||
<span class="text-[15px]">4000</span>
|
<span class="text-[15px]">{{paymentData.estimatedTotal}}</span>
|
||||||
<span class="text-[13px]">人</span>
|
<span class="text-[13px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
<div class="ml-[22px] relative">
|
<div class="ml-[4px] relative">
|
||||||
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
|
<ProportionCharts :chart-data="chartData" class="z-2 relative" />
|
||||||
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-0 z-1" />
|
<SvgComponent :content="paymentChartSvg" class="w-[143px] h-[143px] absolute top-0 left-0 z-1" />
|
||||||
<div
|
<div
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<div class="w-[6px] h-[6px] rounded-full" :style="{ backgroundColor: item.color }"></div>
|
<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">
|
<div class="flex-1 flex items-center text-[#C0EEFF] text-[12px] justify-between">
|
||||||
<span class="ml-[4px] mr-[9px]">{{ item.name }}</span>
|
<span class="ml-[4px] mr-[9px]">{{ item.name }}</span>
|
||||||
<span>{{ item.value }}人</span>
|
<span class="w-max break-keep">{{ item.value }}人</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-image w-full mt-[6px]"></div>
|
<div class="border-image w-full mt-[6px]"></div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -58,12 +58,19 @@
|
||||||
groupSvg.value = svg;
|
groupSvg.value = svg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartData = ref([
|
const paymentData = inject("paymentData",ref({chargeTotal:0,estimatedTotal:0,items:[]}))
|
||||||
{ name: "类型A", value: 250, color: "#0783FA" },
|
const colors = ["#0783FA","#07D1FA","#20E6A4","#FFD15C","#9A68FF"]
|
||||||
{ name: "类型B", value: 274, color: "#07D1FA" },
|
const chartData = ref<any>([]);
|
||||||
{ name: "类型C", value: 310, color: "#20E6A4" },
|
|
||||||
{ name: "类型D", value: 135, color: "#FFD15C" },
|
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 lightningBoxSvg = ref("");
|
||||||
const getLightningBoxSvg = async () => {
|
const getLightningBoxSvg = async () => {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline leading-[1] mt-[29px] mx-[27px]">
|
<div class="flex items-baseline leading-[1] mt-[29px] mx-[27px]">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">总获客</div>
|
<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>
|
<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]" />
|
<SvgIcon name="arrow-up" class="text-[9px] text-[#4AFFA2] ml-[9px]" />
|
||||||
</div>
|
</div>
|
||||||
<SixStatisticsChart />
|
<SixStatisticsChart :chartData="sixStatisticsData.items ||[]"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -56,6 +56,8 @@
|
||||||
moreArrowSvg.value = svg;
|
moreArrowSvg.value = svg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sixStatisticsData = inject("sixStatisticsData",ref<{total:number,items:{data:string,total:number}[]}>({total:0,items:[]}))
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getHeaderLeftSvg();
|
getHeaderLeftSvg();
|
||||||
getHeaderRightSvg();
|
getHeaderRightSvg();
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
<StudentSourceChart
|
<StudentSourceChart
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
:chartData="[
|
:chartData="[
|
||||||
{ name: '线下', value: 240, itemStyle: { color: 'rgba(147, 219, 255, 1)' } },
|
{ name: '线下', value: offlineTotal, itemStyle: { color: 'rgba(147, 219, 255, 1)' } },
|
||||||
{ name: '线上', value: 600, itemStyle: { color: 'rgb(79, 214, 169)' } },
|
{ name: '线上', value: onlineTotal, itemStyle: { color: 'rgb(79, 214, 169)' } },
|
||||||
]"
|
]"
|
||||||
:ringSize="0.8" />
|
:ringSize="0.8" />
|
||||||
<div class="flex items-center justify-between mx-[20px] my-[33px]">
|
<div class="flex items-center justify-between mx-[20px] my-[33px]">
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<SvgComponent :content="onlineSvg" class="w-[53px] h-[38px]" />
|
<SvgComponent :content="onlineSvg" class="w-[53px] h-[38px]" />
|
||||||
<div class="flex flex-col ml-[9px] items-center">
|
<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">
|
<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>
|
<span class="text-[14px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[#C7F0FF] text-[12px]">线上来源</span>
|
<span class="text-[#C7F0FF] text-[12px]">线上来源</span>
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
<SvgComponent :content="offlineSvg" class="w-[53px] h-[38px]" />
|
<SvgComponent :content="offlineSvg" class="w-[53px] h-[38px]" />
|
||||||
<div class="flex flex-col ml-[9px] items-center">
|
<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">
|
<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>
|
<span class="text-[14px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[#C7F0FF] text-[12px]">线下来源</span>
|
<span class="text-[#C7F0FF] text-[12px]">线下来源</span>
|
||||||
|
|
@ -78,6 +78,19 @@
|
||||||
onlineSvg.value = svg
|
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(() => {
|
onBeforeMount(() => {
|
||||||
getHeaderLeftSvg();
|
getHeaderLeftSvg();
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<div class="w-[144px]">
|
<div class="w-[144px]">
|
||||||
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
<div class="relative w-[144px] h-[113px] flex items-center flex-col">
|
||||||
<div class="text-[#44C1EF] italic text-[20px] font-700">今日缴费</div>
|
<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">
|
<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">125</span>
|
<span class="text-[34px] italic">{{paymentData.toDayTotal || 0}}</span>
|
||||||
<span class="text-[18px]">人</span>
|
<span class="text-[18px]">人</span>
|
||||||
</div>
|
</div>
|
||||||
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
<SvgComponent :content="hexagonalBoxSvg" class="box-light absolute" />
|
||||||
|
|
@ -17,11 +17,12 @@
|
||||||
<SvgComponent :content="paymentBorderSvg" class="w-[127px] h-[104px] z-1" />
|
<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">
|
<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-[12px]">昨日缴费人数</p>
|
||||||
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">177人</p>
|
<p class="text-[#C0EEFF] text-[16px] font-500 mt-[18px] mb-[9px]">{{paymentData.yesterdayTotal || 0}}人</p>
|
||||||
<div class="text-[#FF4E4E] text-[12px] flex items-center">
|
<div :class="`${paymentDifferent > 0 ?'text-[#4AFFA2]': 'text-[#FF4E4E]'} text-[12px] flex items-center`">
|
||||||
<span class="">今日较昨日</span>
|
<span class="">今日较昨日</span>
|
||||||
<SvgIcon name="arrow-up" class="text-[9px]" />
|
<SvgIcon :name="paymentDifferent > 0 ? 'arrow-up' : 'arrow-down'" class="text-[9px]" />
|
||||||
<span>12人</span>
|
|
||||||
|
<span>{{Math.abs(paymentDifferent)}}人</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -33,6 +34,7 @@
|
||||||
import SvgComponent from "@/components/SvgComponent.vue";
|
import SvgComponent from "@/components/SvgComponent.vue";
|
||||||
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
|
import SvgIcon from "@/components/svg-icon/SvgIcon.vue";
|
||||||
|
|
||||||
|
const paymentData = inject("paymentData",ref({toDayTotal:0,yesterdayTotal:0}))
|
||||||
|
|
||||||
|
|
||||||
const groupSvg = ref("");
|
const groupSvg = ref("");
|
||||||
|
|
@ -54,6 +56,14 @@
|
||||||
const { default: svg } = await import("/src/assets/svg-img/payment-border.svg?raw");
|
const { default: svg } = await import("/src/assets/svg-img/payment-border.svg?raw");
|
||||||
paymentBorderSvg.value = svg;
|
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(() => {
|
onBeforeMount(() => {
|
||||||
getGroupBackgroundSvg();
|
getGroupBackgroundSvg();
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,27 @@
|
||||||
const columns = [
|
const columns = [
|
||||||
{ field: "rank", header: "名次", align: "justify-center",width:'68px' },
|
{ field: "rank", header: "名次", align: "justify-center",width:'68px' },
|
||||||
{ field: "name", header: "姓名", align: "justify-left",width:'100px' },
|
{ 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 headerLeftSvg = ref("");
|
||||||
const headerRightSvg = ref("");
|
const headerRightSvg = ref("");
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,16 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as echarts from "echarts";
|
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 }) => {
|
const renderItem = (params: any, api: { coord: (arg0: any[]) => any[]; value: (arg0: number) => any; style: () => any }) => {
|
||||||
// 柱子索引值
|
// 柱子索引值
|
||||||
|
|
@ -85,173 +94,232 @@
|
||||||
let myChart: echarts.ECharts | null = null;
|
let myChart: echarts.ECharts | null = null;
|
||||||
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if (chartDom.value) {
|
if (!chartDom.value) return;
|
||||||
myChart = echarts.init(chartDom.value);
|
|
||||||
// 侧面宽度
|
myChart = echarts.init(chartDom.value);
|
||||||
const WIDTH = 10;
|
// 侧面宽度
|
||||||
// 斜角高度
|
const WIDTH = 10;
|
||||||
const OBLIQUE_ANGLE_HEIGHT = 3.5;
|
// 斜角高度
|
||||||
const leftShape = echarts.graphic.extendShape({
|
const OBLIQUE_ANGLE_HEIGHT = 3.5;
|
||||||
buildPath(ctx, shape) {
|
const leftShape = echarts.graphic.extendShape({
|
||||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
|
buildPath(ctx, shape) {
|
||||||
|
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
|
||||||
|
|
||||||
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
||||||
const p2 = [basicsXAxis - WIDTH, bottomYAxis];
|
const p2 = [basicsXAxis - WIDTH, bottomYAxis];
|
||||||
const p3 = [basicsXAxis, bottomYAxis];
|
const p3 = [basicsXAxis, bottomYAxis];
|
||||||
const p4 = [basicsXAxis, topBasicsYAxis];
|
const p4 = [basicsXAxis, topBasicsYAxis];
|
||||||
|
|
||||||
ctx.moveTo(p1[0], p1[1]);
|
ctx.moveTo(p1[0], p1[1]);
|
||||||
ctx.lineTo(p2[0], p2[1]);
|
ctx.lineTo(p2[0], p2[1]);
|
||||||
ctx.lineTo(p3[0], p3[1]);
|
ctx.lineTo(p3[0], p3[1]);
|
||||||
ctx.lineTo(p4[0], p4[1]);
|
ctx.lineTo(p4[0], p4[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const rightShape = echarts.graphic.extendShape({
|
const rightShape = echarts.graphic.extendShape({
|
||||||
buildPath(ctx, shape) {
|
buildPath(ctx, shape) {
|
||||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
|
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
|
||||||
|
|
||||||
const p1 = [basicsXAxis, topBasicsYAxis];
|
const p1 = [basicsXAxis, topBasicsYAxis];
|
||||||
const p2 = [basicsXAxis, bottomYAxis];
|
const p2 = [basicsXAxis, bottomYAxis];
|
||||||
const p3 = [basicsXAxis + WIDTH, bottomYAxis];
|
const p3 = [basicsXAxis + WIDTH, bottomYAxis];
|
||||||
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
||||||
|
|
||||||
ctx.moveTo(p1[0], p1[1]);
|
ctx.moveTo(p1[0], p1[1]);
|
||||||
ctx.lineTo(p2[0], p2[1]);
|
ctx.lineTo(p2[0], p2[1]);
|
||||||
ctx.lineTo(p3[0], p3[1]);
|
ctx.lineTo(p3[0], p3[1]);
|
||||||
ctx.lineTo(p4[0], p4[1]);
|
ctx.lineTo(p4[0], p4[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const topShape = echarts.graphic.extendShape({
|
const topShape = echarts.graphic.extendShape({
|
||||||
buildPath(ctx, shape) {
|
buildPath(ctx, shape) {
|
||||||
const { topBasicsYAxis, basicsXAxis } = shape;
|
const { topBasicsYAxis, basicsXAxis } = shape;
|
||||||
|
|
||||||
const p1 = [basicsXAxis, topBasicsYAxis];
|
const p1 = [basicsXAxis, topBasicsYAxis];
|
||||||
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
||||||
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2];
|
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2];
|
||||||
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
|
||||||
|
|
||||||
ctx.moveTo(p1[0], p1[1]);
|
ctx.moveTo(p1[0], p1[1]);
|
||||||
ctx.lineTo(p2[0], p2[1]);
|
ctx.lineTo(p2[0], p2[1]);
|
||||||
ctx.lineTo(p3[0], p3[1]);
|
ctx.lineTo(p3[0], p3[1]);
|
||||||
ctx.lineTo(p4[0], p4[1]);
|
ctx.lineTo(p4[0], p4[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const middleShape = echarts.graphic.extendShape({
|
const middleShape = echarts.graphic.extendShape({
|
||||||
buildPath(ctx, shape) {
|
buildPath(ctx, shape) {
|
||||||
const { topBasicsYAxis, basicsXAxis, bottomYAxis } = shape;
|
const { topBasicsYAxis, basicsXAxis, bottomYAxis } = shape;
|
||||||
|
|
||||||
const p1 = [basicsXAxis - 0.5, topBasicsYAxis];
|
const p1 = [basicsXAxis - 0.5, topBasicsYAxis];
|
||||||
const p2 = [basicsXAxis - 0.5, bottomYAxis];
|
const p2 = [basicsXAxis - 0.5, bottomYAxis];
|
||||||
const p3 = [basicsXAxis + 0.5, bottomYAxis];
|
const p3 = [basicsXAxis + 0.5, bottomYAxis];
|
||||||
const p4 = [basicsXAxis + 0.5, topBasicsYAxis];
|
const p4 = [basicsXAxis + 0.5, topBasicsYAxis];
|
||||||
|
|
||||||
ctx.moveTo(p1[0], p1[1]);
|
ctx.moveTo(p1[0], p1[1]);
|
||||||
ctx.lineTo(p2[0], p2[1]);
|
ctx.lineTo(p2[0], p2[1]);
|
||||||
ctx.lineTo(p3[0], p3[1]);
|
ctx.lineTo(p3[0], p3[1]);
|
||||||
ctx.lineTo(p4[0], p4[1]);
|
ctx.lineTo(p4[0], p4[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
echarts.graphic.registerShape("leftShape", leftShape);
|
echarts.graphic.registerShape("leftShape", leftShape);
|
||||||
echarts.graphic.registerShape("rightShape", rightShape);
|
echarts.graphic.registerShape("rightShape", rightShape);
|
||||||
echarts.graphic.registerShape("topShape", topShape);
|
echarts.graphic.registerShape("topShape", topShape);
|
||||||
echarts.graphic.registerShape("middleShape", middleShape);
|
echarts.graphic.registerShape("middleShape", middleShape);
|
||||||
|
|
||||||
const options = {
|
// 初始化时设置空数据,后续通过updateChartData更新
|
||||||
grid: {
|
const options = {
|
||||||
top: "12%",
|
grid: {
|
||||||
left: "5%",
|
top: "12%",
|
||||||
right: "3%",
|
left: "5%",
|
||||||
bottom: "15%",
|
right: "3%",
|
||||||
},
|
bottom: "15%",
|
||||||
xAxis: {
|
},
|
||||||
type: "category",
|
xAxis: {
|
||||||
data: [1, 2, 3, 4, 5],
|
type: "category",
|
||||||
axisLabel: {
|
data: [],
|
||||||
formatter: (value: any) => value,
|
axisLabel: {
|
||||||
color: "#C0EEFF",
|
formatter: (value: any) => {
|
||||||
fontSize: 14,
|
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');
|
||||||
},
|
},
|
||||||
nameTextStyle: {
|
color: "#C0EEFF",
|
||||||
|
fontSize: 14,
|
||||||
|
interval: 0,
|
||||||
|
},
|
||||||
|
nameTextStyle: {
|
||||||
|
color: "#243174",
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
color: "#243174",
|
color: "#243174",
|
||||||
},
|
},
|
||||||
axisLine: {
|
},
|
||||||
lineStyle: {
|
},
|
||||||
color: "#243174",
|
yAxis: {
|
||||||
},
|
name: "人数",
|
||||||
|
nameLocation: "end",
|
||||||
|
nameTextStyle: {
|
||||||
|
color: "#C0EEFF",
|
||||||
|
fontSize: 14,
|
||||||
|
padding: [0, 5, 0, 0],
|
||||||
|
align: "right",
|
||||||
|
},
|
||||||
|
type: "value",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
interval: 20,
|
||||||
|
axisLabel: {
|
||||||
|
color: "#C0EEFF",
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: "#243174",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yAxis: {
|
splitLine: {
|
||||||
name: "人数",
|
lineStyle: {
|
||||||
nameLocation: "end",
|
type: "dashed",
|
||||||
data: [100, 200, 300, 400],
|
color: "#308EFF",
|
||||||
nameTextStyle: {
|
width: 1,
|
||||||
color: "#C0EEFF",
|
opacity: 0.2,
|
||||||
fontSize: 14,
|
|
||||||
padding: [0, 5, 0, 0],
|
|
||||||
align: "right",
|
|
||||||
},
|
},
|
||||||
type: "value",
|
},
|
||||||
min: 0,
|
},
|
||||||
max: 500,
|
series: [
|
||||||
interval: 100,
|
{
|
||||||
axisLabel: {
|
type: "custom",
|
||||||
color: "#C0EEFF",
|
renderItem: renderItem,
|
||||||
fontSize: 14,
|
color: "blue",
|
||||||
},
|
data: [],
|
||||||
axisLine: {
|
},
|
||||||
|
{
|
||||||
|
type: "bar",
|
||||||
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
position: "top",
|
||||||
color: "#243174",
|
fontSize: 12,
|
||||||
},
|
color: "rgba(192, 238, 255, 1)",
|
||||||
},
|
},
|
||||||
splitLine: {
|
tooltip: {
|
||||||
lineStyle: {
|
show: false,
|
||||||
type: "dashed",
|
|
||||||
color: "#308EFF",
|
|
||||||
width: 1,
|
|
||||||
opacity: 0.2,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: "transparent",
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
},
|
},
|
||||||
series: [
|
],
|
||||||
{
|
};
|
||||||
type: "custom",
|
|
||||||
renderItem: renderItem,
|
|
||||||
color: "blue",
|
|
||||||
data: [100, 200, 300, 400],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "bar",
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: "top",
|
|
||||||
fontSize: 12,
|
|
||||||
color: "rgba(192, 238, 255, 1)",
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: "transparent",
|
|
||||||
},
|
|
||||||
data: [100, 200, 300, 400],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
myChart.setOption(options);
|
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 = () => {
|
const handleResize = () => {
|
||||||
myChart?.resize();
|
myChart?.resize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 监听数据变化,只更新数据不重新创建图表
|
||||||
|
watch(
|
||||||
|
() => props.chartData,
|
||||||
|
() => {
|
||||||
|
if (myChart) {
|
||||||
|
updateChartData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart();
|
initChart();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,136 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref, watch, computed } from "vue";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
const chartDom = ref(null);
|
const chartDom = ref(null);
|
||||||
let myChart: echarts.ECharts | null = null;
|
let myChart: echarts.ECharts | null = null;
|
||||||
|
|
||||||
// 图表数据
|
const props = defineProps({
|
||||||
const chartData = ref({
|
chartDataArray: {
|
||||||
dates: ["5.24", "5.25", "5.26", "5.27", "5.28", "5.29", "5.30"],
|
type: Array,
|
||||||
seriesData: [
|
default: () => []
|
||||||
{ 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 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") {
|
||||||
|
// 如果数据超过30天,显示最近30天
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用computed属性基于props生成图表数据
|
||||||
|
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");
|
const currentMode = ref("week");
|
||||||
|
|
@ -70,7 +186,7 @@
|
||||||
|
|
||||||
// 生成图表系列的函数
|
// 生成图表系列的函数
|
||||||
const generateSeries = (seriesData: any) => {
|
const generateSeries = (seriesData: any) => {
|
||||||
return seriesData.map((item: any,) => {
|
return seriesData.map((item: any) => {
|
||||||
const series = {
|
const series = {
|
||||||
name: item.name,
|
name: item.name,
|
||||||
type: "line",
|
type: "line",
|
||||||
|
|
@ -94,16 +210,7 @@
|
||||||
// 切换周/月显示
|
// 切换周/月显示
|
||||||
const switchDisplayMode = (mode: string) => {
|
const switchDisplayMode = (mode: string) => {
|
||||||
currentMode.value = mode;
|
currentMode.value = mode;
|
||||||
|
emits("dateChange", 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月"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新渲染图表
|
// 重新渲染图表
|
||||||
initChart();
|
initChart();
|
||||||
};
|
};
|
||||||
|
|
@ -352,27 +459,26 @@
|
||||||
color: "transparent",
|
color: "transparent",
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
myChart.setOption(option, true);
|
myChart.setOption(option, true);
|
||||||
|
|
||||||
// 跟踪鼠标指示器状态
|
// 跟踪鼠标指示器状态
|
||||||
const hoverBarId = 'mouseHoverBar';
|
const hoverBarId = "mouseHoverBar";
|
||||||
|
|
||||||
|
myChart.on("mouseover", { seriesId: "axisOverlayBar" }, (params) => {
|
||||||
myChart.on("mouseover", { seriesId: "axisOverlayBar" }, (params) => {
|
|
||||||
if (!myChart) return;
|
if (!myChart) return;
|
||||||
|
|
||||||
// 获取当前数据索引
|
// 获取当前数据索引
|
||||||
const dataIndex = params.dataIndex;
|
const dataIndex = params.dataIndex;
|
||||||
|
|
||||||
// 获取当前option配置
|
// 获取当前option配置
|
||||||
const _option = myChart.getOption() as any;
|
const _option = myChart.getOption() as any;
|
||||||
|
|
||||||
// 检查是否已存在高亮条
|
// 检查是否已存在高亮条
|
||||||
let seriesIndex = -1;
|
let seriesIndex = -1;
|
||||||
if (_option.series && Array.isArray(_option.series)) {
|
if (_option.series && Array.isArray(_option.series)) {
|
||||||
|
|
@ -382,22 +488,22 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建高亮条配置
|
// 构建高亮条配置
|
||||||
const hoverBarOption = {
|
const hoverBarOption = {
|
||||||
id: hoverBarId,
|
id: hoverBarId,
|
||||||
type: 'bar',
|
type: "bar",
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
barGap: '-100%',
|
barGap: "-100%",
|
||||||
zlevel: 10,
|
zlevel: 10,
|
||||||
silent: true,
|
silent: true,
|
||||||
animation: true,
|
animation: true,
|
||||||
data: chartData.value.dates.map((_: any, index: number) => {
|
data: chartData.value.dates.map((_: any, index: number) => {
|
||||||
return index === dataIndex ? 500 : '-'
|
return index === dataIndex ? 500 : "-";
|
||||||
}),
|
}),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: {
|
color: {
|
||||||
type: 'linear',
|
type: "linear",
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
x2: 0,
|
x2: 0,
|
||||||
|
|
@ -405,22 +511,22 @@
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{
|
||||||
offset: 0,
|
offset: 0,
|
||||||
color: '#fff' // 开始颜色
|
color: "#fff", // 开始颜色
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offset: 1,
|
offset: 1,
|
||||||
color: '#0783FA' // 结束颜色
|
color: "#0783FA", // 结束颜色
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
global: false
|
global: false,
|
||||||
},
|
},
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (seriesIndex === -1) {
|
if (seriesIndex === -1) {
|
||||||
// 如果不存在则添加新的高亮条
|
// 如果不存在则添加新的高亮条
|
||||||
if (_option.series && Array.isArray(_option.series)) {
|
if (_option.series && Array.isArray(_option.series)) {
|
||||||
|
|
@ -432,32 +538,31 @@
|
||||||
_option.series[seriesIndex].data = hoverBarOption.data;
|
_option.series[seriesIndex].data = hoverBarOption.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新图表
|
// 更新图表
|
||||||
myChart.setOption(_option, false);
|
myChart.setOption(_option, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 鼠标移出图表时移除高亮条
|
// 鼠标移出图表时移除高亮条
|
||||||
myChart.on("mouseout", { seriesId: "axisOverlayBar" }, () => {
|
myChart.on("mouseout", { seriesId: "axisOverlayBar" }, () => {
|
||||||
|
|
||||||
if (!myChart) return;
|
if (!myChart) return;
|
||||||
const _option = myChart.getOption() as any;
|
const _option = myChart.getOption() as any;
|
||||||
const hoverBarExists = _option.series.find((series: any) => series.id && series.id === hoverBarId);
|
const hoverBarExists = _option.series.find((series: any) => series.id && series.id === hoverBarId);
|
||||||
|
|
||||||
if (hoverBarExists) {
|
if (hoverBarExists) {
|
||||||
try {
|
try {
|
||||||
// 检查series是否存在
|
// 检查series是否存在
|
||||||
if (_option.series && Array.isArray(_option.series)) {
|
if (_option.series && Array.isArray(_option.series)) {
|
||||||
// 查找并删除高亮条
|
// 查找并删除高亮条
|
||||||
const newSeries = _option.series.filter((series: any) => {
|
const newSeries = _option.series.filter((series: any) => {
|
||||||
return series.id !== hoverBarId;
|
return series.id !== hoverBarId;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 直接设置新的series数组
|
// 直接设置新的series数组
|
||||||
_option.series = newSeries;
|
_option.series = newSeries;
|
||||||
// 更新图表
|
// 更新图表
|
||||||
myChart.setOption(_option, true);
|
myChart.setOption(_option, true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("删除高亮条出错:", err);
|
console.error("删除高亮条出错:", err);
|
||||||
}
|
}
|
||||||
|
|
@ -471,9 +576,13 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart();
|
|
||||||
window.addEventListener("resize", () => myChart?.resize());
|
window.addEventListener("resize", () => myChart?.resize());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听图表数据变化,更新图表
|
||||||
|
watch(() => props.chartDataArray, () => {
|
||||||
|
initChart();
|
||||||
|
}, { deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -1,125 +1,143 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-[143px] h-[143px]">
|
<div class="w-[143px] h-[143px]">
|
||||||
<div class="w-[143px] h-[143px] bg-transparent" ref="chartRef"></div>
|
<div class="w-[143px] h-[143px] bg-transparent" ref="chartRef"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
// 图表容器引用
|
// 图表容器引用
|
||||||
const chartRef = ref<HTMLElement | null>(null)
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
let chartInstance: echarts.ECharts | null = null
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
|
|
||||||
// 接收数据作为prop
|
// 接收数据作为prop
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
chartData: {
|
chartData: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
default: () => []
|
default: () => [],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
// 初始化图表
|
const generatedFormattedData = () => {
|
||||||
const initChart = () => {
|
const formattedData = props.chartData.map((item: any) => ({
|
||||||
|
...item,
|
||||||
|
// 确保每个项都有itemStyle且包含color
|
||||||
|
itemStyle: {
|
||||||
|
color: item.color || (item.itemStyle && item.itemStyle.color),
|
||||||
|
borderWidth: 0, // 移除边框以消除间隔
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
return formattedData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
if (chartRef.value) {
|
if (chartRef.value) {
|
||||||
// 销毁之前的实例
|
// 销毁之前的实例
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose()
|
chartInstance.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新实例
|
// 创建新实例
|
||||||
chartInstance = echarts.init(chartRef.value)
|
chartInstance = echarts.init(chartRef.value);
|
||||||
|
|
||||||
// 配置颜色从数据中获取
|
// 配置颜色从数据中获取
|
||||||
const formattedData = props.chartData.map((item: any) => ({
|
|
||||||
...item,
|
// 配置项
|
||||||
// 确保每个项都有itemStyle且包含color
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: "item",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
||||||
|
borderColor: "#44F1FF",
|
||||||
|
textStyle: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 10,
|
||||||
|
},
|
||||||
|
formatter: "{b}: {d}%",
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "数据占比",
|
||||||
|
type: "pie",
|
||||||
|
radius: ["70%", "90%"],
|
||||||
|
center: ["50%", "50%"],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: item.color || (item.itemStyle && item.itemStyle.color),
|
borderWidth: 0, // 移除扇区边框
|
||||||
borderWidth: 0 // 移除边框以消除间隔
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 配置项
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
||||||
borderColor: '#44F1FF',
|
|
||||||
textStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 10
|
|
||||||
},
|
|
||||||
formatter: '{b}: {d}%',
|
|
||||||
|
|
||||||
},
|
},
|
||||||
series: [
|
label: {
|
||||||
{
|
show: false,
|
||||||
name: '数据占比',
|
},
|
||||||
type: 'pie',
|
emphasis: {
|
||||||
radius: ['70%', '90%'],
|
scale: false,
|
||||||
center: ['50%', '50%'],
|
itemStyle: {
|
||||||
avoidLabelOverlap: false,
|
shadowBlur: 5,
|
||||||
itemStyle: {
|
shadowOffsetX: 0,
|
||||||
borderWidth: 0 // 移除扇区边框
|
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||||
},
|
},
|
||||||
label: {
|
},
|
||||||
show: false
|
labelLine: {
|
||||||
},
|
show: false,
|
||||||
emphasis: {
|
},
|
||||||
scale: false,
|
data: generatedFormattedData(),
|
||||||
itemStyle: {
|
// 移除间隙
|
||||||
shadowBlur: 5,
|
gap: 0,
|
||||||
shadowOffsetX: 0,
|
},
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
],
|
||||||
}
|
animation: true,
|
||||||
},
|
animationDuration: 500,
|
||||||
labelLine: {
|
};
|
||||||
show: false
|
|
||||||
},
|
|
||||||
data: formattedData,
|
|
||||||
// 移除间隙
|
|
||||||
gap: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
animation: true,
|
|
||||||
animationDuration: 500
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置配置项
|
|
||||||
chartInstance.setOption(option)
|
|
||||||
|
|
||||||
// 监听窗口大小变化
|
|
||||||
window.addEventListener('resize', handleResize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理窗口大小变化
|
// 设置配置项
|
||||||
const handleResize = () => {
|
chartInstance.setOption(option);
|
||||||
|
|
||||||
|
// 监听窗口大小变化
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理窗口大小变化
|
||||||
|
const handleResize = () => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize()
|
chartInstance.resize();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 生命周期钩子
|
// 生命周期钩子
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart()
|
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) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose()
|
chartInstance.dispose();
|
||||||
chartInstance = null
|
chartInstance = null;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,17 @@ echarts.use([
|
||||||
const chartRef = useTemplateRef("chartRef")
|
const chartRef = useTemplateRef("chartRef")
|
||||||
let chart: echarts.ECharts | null = null;
|
let chart: echarts.ECharts | null = null;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
chartData:{
|
||||||
|
type:Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.chartData,() => {
|
||||||
|
initChart();
|
||||||
|
})
|
||||||
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if(!chartRef.value) return;
|
if(!chartRef.value) return;
|
||||||
chart = echarts.init(chartRef.value)
|
chart = echarts.init(chartRef.value)
|
||||||
|
|
@ -42,7 +53,7 @@ const initChart = () => {
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
data: props.chartData.map((item:any) => item.date),
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
|
@ -73,7 +84,7 @@ const initChart = () => {
|
||||||
name: '数据1',
|
name: '数据1',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
data: [120, 132, 101, 134, 90, 230, 210],
|
data: props.chartData.map((item:any) => item.total),
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(41, 241, 250, 1)',
|
color: 'rgba(41, 241, 250, 1)',
|
||||||
width: 2
|
width: 2
|
||||||
|
|
@ -103,7 +114,7 @@ const initChart = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart();
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
chart?.resize();
|
chart?.resize();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
|
|
||||||
const props = withDefaults(defineProps<DefaultProps>(), {
|
const props = withDefaults(defineProps<DefaultProps>(), {
|
||||||
chartData: [
|
chartData: [
|
||||||
{ name: "线下", value: 310, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
|
{ name: "线下", value: 0, itemStyle: { color: "rgba(147, 219, 255, 1)" } },
|
||||||
{ name: "线上", value: 335, itemStyle: { color: "rgb(79, 214, 169)" } },
|
{ name: "线上", value: 0, itemStyle: { color: "rgb(79, 214, 169)" } },
|
||||||
],
|
],
|
||||||
ringSize: 1,
|
ringSize: 1,
|
||||||
});
|
});
|
||||||
|
|
@ -206,6 +206,7 @@
|
||||||
height: 106,
|
height: 106,
|
||||||
},
|
},
|
||||||
z: 0,
|
z: 0,
|
||||||
|
cursor:"point",
|
||||||
origin: [127, 53],
|
origin: [127, 53],
|
||||||
keyframeAnimation: [
|
keyframeAnimation: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<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">
|
<header class="relative flex items-center">
|
||||||
<SvgComponent :content="headerSvg" class="w-full h-[98px]" />
|
<SvgComponent :content="headerSvg" class="w-full h-[98px]" />
|
||||||
<SvgComponent :content="titleSvg" class="w-[50%] h-[69px] absolute top-0 left-50% translate-x-[-50%]" />
|
<SvgComponent :content="titleSvg" class="w-[50%] h-[69px] absolute top-0 left-50% translate-x-[-50%]" />
|
||||||
|
|
@ -8,9 +8,9 @@
|
||||||
<DigitalWatch class="ml-[10px]" />
|
<DigitalWatch class="ml-[10px]" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="flex items-center justify-end pr-[24px] cursor-pointer mb-[13px]">
|
<div class="flex items-center justify-end pr-[24px] cursor-pointer mb-[13px]" @click="updateAllData">
|
||||||
<SvgIcon name="circle" class="text-[14px] text-[#C0EEFF] hover:rotate-90 transition-all duration-300" />
|
<SvgIcon name="circle" class="text-[14px] text-[#C0EEFF] transition-all duration-300"/>
|
||||||
<div class="text-[#C0EEFF] text-[12px] ml-[5px]">数据更新时间:6.12 12:00:00</div>
|
<div class="text-[#C0EEFF] text-[12px] ml-[5px]">数据更新时间:{{ formatDatetime(updateTime) }}</div>
|
||||||
</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]">
|
<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]" />
|
<LossStatic class="ml-[20px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-[24px] justify-start">
|
<div class="flex items-center px-[24px] justify-start">
|
||||||
<OperatingTrends class=""/>
|
<OperatingTrends class="" />
|
||||||
<AskSection class="ml-[20px]"/>
|
<AskSection class="ml-[20px]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-[24px] overflow-x-auto">
|
<div class="flex items-center px-[24px] overflow-x-auto">
|
||||||
<StudentSource />
|
<StudentSource />
|
||||||
<OnLineStatus class="ml-[20px]"/>
|
<OnLineStatus class="ml-[20px]" />
|
||||||
<OfflineStatus class="ml-[20px]" />
|
<OfflineStatus class="ml-[20px]" />
|
||||||
<SixStatistics class="ml-[20px]"/>
|
<SixStatistics class="ml-[20px]" />
|
||||||
<ChargingRanking class="ml-[20px]" />
|
<ChargingRanking class="ml-[20px]" />
|
||||||
<WinCustomer class="ml-[20px]" />
|
<WinCustomer class="ml-[20px]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,11 +55,14 @@
|
||||||
import SixStatistics from "@/views/components/SixStatistics.vue";
|
import SixStatistics from "@/views/components/SixStatistics.vue";
|
||||||
import ChargingRanking from "./components/ChargingRanking.vue";
|
import ChargingRanking from "./components/ChargingRanking.vue";
|
||||||
import WinCustomer from "./components/WinCustomer.vue";
|
import WinCustomer from "./components/WinCustomer.vue";
|
||||||
|
|
||||||
import { useDate } from "@/composables/useDate";
|
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 headerSvg = ref("");
|
||||||
const headerBackgroundSvg = async () => {
|
const headerBackgroundSvg = async () => {
|
||||||
|
|
@ -74,14 +77,17 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useFetchAllData()
|
||||||
|
|
||||||
|
const updateAllData = () => {
|
||||||
|
runImmediatelyAll()
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
updateTime();
|
formateTime();
|
||||||
headerBackgroundSvg();
|
headerBackgroundSvg();
|
||||||
headerTitleSvg();
|
headerTitleSvg();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
@ -92,5 +98,4 @@
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue