index.vue 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775
  1. <template>
  2. <div class="home-container">
  3. <!-- 添加英雄联盟数据可视化部分 -->
  4. <el-row :gutter="15" class="lol-data-section">
  5. <el-col :span="24">
  6. <div class="lol-container">
  7. <div class="head">
  8. <div class="weather">
  9. <span id="showTime"></span>
  10. <el-button class="fullscreen-btn" type="text" @click="toggleFullScreen">
  11. <el-icon><FullScreen v-if="!isFullscreen"/><Aim v-else/></el-icon>
  12. </el-button>
  13. <el-button class="settings-btn" type="text" @click="openSettings">
  14. <el-icon><Setting /></el-icon>
  15. </el-button>
  16. </div>
  17. <h1>设备运维数据中心</h1>
  18. </div>
  19. <div class="mainbox">
  20. <ul class="clearfix">
  21. <li>
  22. <div v-if="panelSettings.deviceUtilization" class="boxall" :style="{ flex: !panelSettings.deviceRanking ? 3.5 : 2 }">
  23. <div class="alltitle">设备利用率趋势</div>
  24. <div class="navboxall" id="echart5" style="height: 95%;"></div>
  25. </div>
  26. <div v-if="panelSettings.deviceRanking" class="boxall" :style="{ flex: !panelSettings.deviceUtilization ? 3.5 : 1.5, overflow: 'hidden' }">
  27. <div class="alltitle">单台设备累计借用时长排名</div>
  28. <div class="navboxall">
  29. <div class="wraptit">
  30. <span>排名</span><span>设备名称</span><span>设备编号</span><span>借用时长</span>
  31. </div>
  32. <div class="wrap" style="height: 85%">
  33. <ul>
  34. <li v-for="(hero, index) in heroData" :key="index" style="width: 100%;">
  35. <div
  36. style="display: flex;justify-content: space-between; width: 100%;">
  37. <span>{{ index + 1 }}</span>
  38. <span>{{ hero.device_name }}</span>
  39. <span>{{ hero.device_code }}</span>
  40. <span>{{ hero.total_duration }}</span>
  41. </div>
  42. </li>
  43. </ul>
  44. </div>
  45. </div>
  46. </div>
  47. <!-- <div class="boxall" style="height:260px; overflow: hidden;">
  48. <div class="alltitle">设备故障统计</div>
  49. <div class="navboxall" id="echart1" style="height: 95%;"></div>
  50. </div> -->
  51. </li>
  52. <li>
  53. <div v-if="panelSettings.coreMetrics" class="boxall" :style="{ height: !panelSettings.borrowTrend ? '700px' : '360px' }">
  54. <div class="alltitle">核心概览指标</div>
  55. <div class="clearfix navboxall" style="height: 100%">
  56. <div class="data-overview">
  57. <div class="data-card total-devices">
  58. <div class="card-content">
  59. <div class="card-title">设备总数</div>
  60. <div class="card-value">{{ devicedata?.total_devices ?? 0 }}<span
  61. class="card-unit">个</span></div>
  62. </div>
  63. </div>
  64. <div class="data-card borrowed">
  65. <div class="card-content">
  66. <div class="card-title">已借出</div>
  67. <div class="card-value">{{ devicedata?.borrowed_devices ?? 0 }}<span
  68. class="card-unit">个</span></div>
  69. </div>
  70. </div>
  71. <div class="data-card available">
  72. <div class="card-content">
  73. <div class="card-title">可用设备</div>
  74. <div class="card-value">{{ devicedata?.available_devices ?? 0 }}<span
  75. class="card-unit">个</span></div>
  76. </div>
  77. </div>
  78. <div class="data-card today-borrowed">
  79. <div class="card-content">
  80. <div class="card-title">今日归还</div>
  81. <div class="card-value">{{ devicedata?.today_returned ?? 0 }}<span
  82. class="card-unit">个</span></div>
  83. </div>
  84. </div>
  85. <div class="data-card today-returned">
  86. <div class="card-content">
  87. <div class="card-title">今日借出</div>
  88. <div class="card-value">{{ devicedata?.today_borrowed ?? 0 }}个</div>
  89. </div>
  90. </div>
  91. <div class="data-card total-borrows">
  92. <div class="card-content">
  93. <div class="card-title">累计借用次数</div>
  94. <div class="card-value">{{ devicedata?.total_borrows ?? 0 }}<span
  95. class="card-unit">个</span></div>
  96. <!-- <div class="card-subtitle">bobel (借出总数)</div> -->
  97. </div>
  98. </div>
  99. </div>
  100. </div>
  101. </div>
  102. <div v-if="panelSettings.borrowTrend" class="boxall" :style="{ height: !panelSettings.coreMetrics ? '700px' : '340px' }">
  103. <div class="alltitle">整体设备借用次数趋势</div>
  104. <div class="navboxall" id="echart3" style="height: 95%;"></div>
  105. </div>
  106. <!-- <div class="boxall" style="height:350px">
  107. <div class="alltitle">设备实时执行情况</div>
  108. <div class="navboxall" id="echart4" style="height: 95%;"></div>
  109. </div> -->
  110. </li>
  111. <li>
  112. <div class="timeselect" style="height:30px">
  113. <!-- 快捷时间选择 -->
  114. <el-radio-group class="transparent-radio" v-model="quickRange"
  115. @change="handleQuickRange">
  116. <el-radio-button label="day">今日</el-radio-button>
  117. <el-radio-button label="month">本月</el-radio-button>
  118. <el-radio-button label="year">今年</el-radio-button>
  119. </el-radio-group>
  120. </div>
  121. <div v-if="panelSettings.deviceBorrowRanking" class="boxall" :style="{ height: !panelSettings.activeUsers ? '670px' : '350px', overflow: 'hidden' }">
  122. <div class="alltitle">单台设备累计借用次数排名</div>
  123. <div class="navboxall">
  124. <div class="filter-bar"
  125. style="margin-bottom: 10px; display: flex; gap: 12px; align-items: center;">
  126. <!-- 借用类型筛选 -->
  127. <el-select class="transparent-select" v-model="selectedType"
  128. placeholder="借用类型" @change="fetchRankings">
  129. <el-option label="全部" value="all" />
  130. <el-option label="常规借用" value="regular" />
  131. <el-option label="课堂借用" value="classroom" />
  132. <el-option label="特殊借用" value="special" />
  133. </el-select>
  134. <!-- 时间范围筛选 -->
  135. <el-date-picker v-model="dateRange" type="daterange"
  136. start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD"
  137. value-format="YYYY-MM-DD" @change="fetchRankings" />
  138. <el-select clearable v-model="deviceTagId" placeholder="标签类型"
  139. @change="fetchRankings">
  140. <el-option v-for="item in labelType" :key="item.id" :label="item.name" :value="item.id" />
  141. </el-select>
  142. </div>
  143. <el-table :data="teamRankings" style="width: 100%;height: 75%;">
  144. <el-table-column type="index" label="排名" width="80" />
  145. <el-table-column prop="device_name" label="设备名称" />
  146. <!-- <el-table-column prop="device_code" label="设备编码" /> -->
  147. <el-table-column prop="device_name" label="标签名">
  148. <template #default="scope">
  149. <span v-for="tag in scope.row.device_tags" :key="tag" :type="tag.type">{{' '+tag.name }}</span>
  150. </template>
  151. </el-table-column>
  152. <el-table-column prop="borrow_count" label="借用次数" />
  153. </el-table>
  154. </div>
  155. </div>
  156. <div v-if="panelSettings.activeUsers" class="boxall" :style="{ height: !panelSettings.deviceBorrowRanking ? '670px' : '320px' }">
  157. <div class="alltitle">活跃用户</div>
  158. <div class="navboxall">
  159. <div class="wraptit">
  160. <span>排名</span><span>用户名称</span><span>总借用次数</span>
  161. </div>
  162. <div class="wrap" style="height:85%;">
  163. <ul>
  164. <li v-for="(hero, index) in userList" :key="index" style="width: 100%;">
  165. <div
  166. style="display: flex;justify-content: space-between; width: 100%;">
  167. <span>{{ index + 1 }}</span>
  168. <span>{{ hero.user_name }}</span>
  169. <span>{{ hero.borrow_count }}</span>
  170. </div>
  171. </li>
  172. </ul>
  173. </div>
  174. </div>
  175. </div>
  176. <!-- <div class="boxall" style="height:300px">
  177. <div class="alltitle">设备维修时间统计</div>
  178. </div> -->
  179. </li>
  180. </ul>
  181. </div>
  182. </div>
  183. </el-col>
  184. </el-row>
  185. <!-- 设置弹窗 -->
  186. <el-dialog
  187. v-model="settingsDialogVisible"
  188. title="面板设置"
  189. width="500px"
  190. :close-on-click-modal="false"
  191. destroy-on-close
  192. >
  193. <div class="panel-settings">
  194. <el-checkbox v-model="panelSettings.deviceUtilization">设备利用率趋势</el-checkbox>
  195. <el-checkbox v-model="panelSettings.deviceRanking">单台设备累计借用时长排名</el-checkbox>
  196. <el-checkbox v-model="panelSettings.coreMetrics">核心概览指标</el-checkbox>
  197. <el-checkbox v-model="panelSettings.borrowTrend">整体设备借用次数趋势</el-checkbox>
  198. <el-checkbox v-model="panelSettings.deviceBorrowRanking">单台设备累计借用次数排名</el-checkbox>
  199. <el-checkbox v-model="panelSettings.activeUsers">活跃用户</el-checkbox>
  200. </div>
  201. <template #footer>
  202. <span class="dialog-footer">
  203. <el-button @click="() => state.settingsDialogVisible = false">取消</el-button>
  204. <el-button type="primary" @click="saveSettings">确定</el-button>
  205. </span>
  206. </template>
  207. </el-dialog>
  208. </div>
  209. </template>
  210. <script lang="ts">
  211. import { toRefs, reactive, defineComponent, onMounted, onUnmounted, ref, watch, nextTick, onActivated, computed } from 'vue';
  212. import * as echarts from 'echarts';
  213. import { storeToRefs } from 'pinia';
  214. import { useThemeConfig } from '/@/stores/themeConfig';
  215. import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
  216. import * as api from './api';
  217. import { useRouter } from 'vue-router';
  218. import { request } from '/@/utils/service';
  219. import dayjs from 'dayjs';
  220. import { GetList } from '../devicelabel/api'
  221. import { FullScreen, Aim, Setting } from '@element-plus/icons-vue'
  222. // 定义API数据接口
  223. interface ApiData {
  224. total_applications: number;
  225. total_positions: number;
  226. total_questions: number;
  227. total_reports: number;
  228. application_trend: Array<{
  229. date: string;
  230. count: number;
  231. }>;
  232. }
  233. let global: any = {
  234. homeChartOne: null,
  235. homeChartTwo: null,
  236. homeCharThree: null,
  237. dispose: [null, '', undefined],
  238. };
  239. export default defineComponent({
  240. name: 'home',
  241. components: {
  242. FullScreen,
  243. Aim
  244. },
  245. setup() {
  246. const scrollOffset = ref(0)
  247. const isFullscreen = ref(false)
  248. // 切换全屏
  249. const toggleFullScreen = () => {
  250. const element = document.querySelector('.home-container') as HTMLElement;
  251. if (!element) return;
  252. if (!document.fullscreenElement) {
  253. element.requestFullscreen().then(() => {
  254. isFullscreen.value = true;
  255. }).catch(err => {
  256. console.error(`全屏错误: ${err.message}`);
  257. });
  258. } else {
  259. document.exitFullscreen().then(() => {
  260. isFullscreen.value = false;
  261. }).catch(err => {
  262. console.error(`退出全屏错误: ${err.message}`);
  263. });
  264. }
  265. };
  266. let intervalId: number | null = null
  267. const rowHeight = 50
  268. const visibleRows = 6
  269. const selectedType = ref('all');
  270. const dateRange = ref<[string, string] | null>(null);
  271. const deviceTagId=ref('')
  272. const quickRange = ref('');
  273. const labelType=ref([])
  274. const homeLineRef = ref();
  275. const homePieRef = ref();
  276. const homeBarRef = ref();
  277. const storesTagsViewRoutes = useTagsViewRoutes();
  278. const storesThemeConfig = useThemeConfig();
  279. const { themeConfig } = storeToRefs(storesThemeConfig);
  280. const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
  281. const state = reactive({
  282. devicedata: {
  283. total_devices: 0,
  284. borrowed_devices: 0,
  285. available_devices: 0,
  286. today_borrowed: 0,
  287. today_returned: 0,
  288. total_borrows: 0
  289. },
  290. panelSettings: {
  291. deviceUtilization: true, // 设备利用率趋势
  292. deviceRanking: true, // 单台设备累计借用时长排名
  293. coreMetrics: true, // 核心概览指标
  294. borrowTrend: true, // 整体设备借用次数趋势
  295. deviceBorrowRanking: true, // 单台设备累计借用次数排名
  296. activeUsers: true, // 活跃用户
  297. },
  298. settingsDialogVisible: false,
  299. homeOne: [],
  300. homeThree: [],
  301. myCharts: [],
  302. charts: {
  303. theme: '',
  304. bgColor: '',
  305. color: '#303133',
  306. },
  307. apiData: null as ApiData | null,
  308. heroData: [],
  309. userList: [],/* 用户统计列表 */
  310. teamRankings: []
  311. });
  312. const router = useRouter();
  313. // 折线图
  314. const initLineChart = () => {
  315. if (!global.dispose.some((b: any) => b === global.homeChartOne)) global.homeChartOne.dispose();
  316. global.homeChartOne = <any>echarts.init(homeLineRef.value, state.charts.theme);
  317. const option = {
  318. backgroundColor: state.charts.bgColor,
  319. title: {
  320. text: '申请趋势',
  321. x: 'left',
  322. textStyle: { fontSize: '15', color: state.charts.color },
  323. },
  324. grid: { top: 70, right: 20, bottom: 30, left: 30 },
  325. tooltip: { trigger: 'axis' },
  326. legend: { data: ['申请数量'], right: 0 },
  327. xAxis: {
  328. data: state.apiData?.application_trend?.map((item: { date: string; count: number }) => item.date.substring(5)) ||
  329. ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
  330. },
  331. yAxis: [
  332. {
  333. type: 'value',
  334. name: '数量',
  335. splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
  336. },
  337. ],
  338. series: [
  339. {
  340. name: '申请数量',
  341. type: 'line',
  342. symbolSize: 6,
  343. symbol: 'circle',
  344. smooth: true,
  345. data: state.apiData?.application_trend?.map((item: { date: string; count: number }) => item.count) ||
  346. [0, 41.1, 30.4, 65.1, 53.3, 53.3, 53.3, 41.1, 30.4, 65.1, 53.3, 10],
  347. lineStyle: { color: '#fe9a8b' },
  348. itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' },
  349. areaStyle: {
  350. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  351. { offset: 0, color: '#fe9a8bb3' },
  352. { offset: 1, color: '#fe9a8b03' },
  353. ]),
  354. },
  355. }
  356. ],
  357. };
  358. (<any>global.homeChartOne).setOption(option);
  359. (<any>state.myCharts).push(global.homeChartOne);
  360. };
  361. // 饼图
  362. const initPieChart = () => {
  363. if (!global.dispose.some((b: any) => b === global.homeChartTwo)) global.homeChartTwo.dispose();
  364. global.homeChartTwo = <any>echarts.init(homePieRef.value, state.charts.theme);
  365. var getname = ['申请总数', '职位总数', '问题总数', '报告总数'];
  366. var getvalue = [];
  367. if (state.apiData) {
  368. getvalue = [
  369. state.apiData.total_applications,
  370. state.apiData.total_positions,
  371. state.apiData.total_questions,
  372. state.apiData.total_reports
  373. ];
  374. } else {
  375. getvalue = [34.2, 38.87, 17.88, 9.05];
  376. }
  377. var data = [];
  378. for (var i = 0; i < getname.length; i++) {
  379. data.push({ name: getname[i], value: getvalue[i] });
  380. }
  381. const colorList = ['#51A3FC', '#36C78B', '#FEC279', '#968AF5', '#E790E8'];
  382. const option = {
  383. backgroundColor: state.charts.bgColor,
  384. title: {
  385. text: '数据统计',
  386. x: 'left',
  387. textStyle: { fontSize: '15', color: state.charts.color },
  388. },
  389. tooltip: { trigger: 'item', formatter: '{b} <br/> {c}%' },
  390. graphic: {
  391. elements: [
  392. {
  393. type: 'image',
  394. z: -1,
  395. style: {
  396. image: themeConfig.value.isIsDark
  397. ? ''
  398. : 'data:image/png;base64,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',
  399. width: 230,
  400. height: 230,
  401. },
  402. left: '16.5%',
  403. top: 'center',
  404. },
  405. ],
  406. },
  407. legend: {
  408. type: 'scroll',
  409. orient: 'vertical',
  410. right: '0%',
  411. left: '65%',
  412. top: 'center',
  413. itemWidth: 14,
  414. itemHeight: 14,
  415. data: getname,
  416. textStyle: {
  417. rich: {
  418. name: {
  419. fontSize: 14,
  420. fontWeight: 400,
  421. width: 200,
  422. height: 35,
  423. padding: [0, 0, 0, 60],
  424. color: state.charts.color,
  425. },
  426. rate: {
  427. fontSize: 15,
  428. fontWeight: 500,
  429. height: 35,
  430. width: 40,
  431. padding: [0, 0, 0, 30],
  432. color: state.charts.color,
  433. },
  434. },
  435. },
  436. },
  437. series: [
  438. {
  439. type: 'pie',
  440. radius: ['82', themeConfig.value.isIsDark ? '50' : '102'],
  441. center: ['32%', '50%'],
  442. itemStyle: {
  443. color: function (params: any) {
  444. return colorList[params.dataIndex];
  445. },
  446. },
  447. label: { show: false },
  448. labelLine: { show: false },
  449. data: data,
  450. },
  451. ],
  452. };
  453. (<any>global.homeChartTwo).setOption(option);
  454. (<any>state.myCharts).push(global.homeChartTwo);
  455. };
  456. // 柱状图
  457. const initBarChart = () => {
  458. if (!global.dispose.some((b: any) => b === global.homeCharThree)) global.homeCharThree.dispose();
  459. global.homeCharThree = <any>echarts.init(homeBarRef.value, state.charts.theme);
  460. const option = {
  461. backgroundColor: state.charts.bgColor,
  462. title: {
  463. text: '设备使用趋势',
  464. x: 'left',
  465. textStyle: { fontSize: '15', color: state.charts.color },
  466. },
  467. tooltip: { trigger: 'axis' },
  468. legend: { data: ['使用率'], right: 0 },
  469. grid: { top: 70, right: 80, bottom: 30, left: 80 },
  470. xAxis: [
  471. {
  472. type: 'category',
  473. data: [],
  474. boundaryGap: true,
  475. axisTick: { show: false },
  476. },
  477. ],
  478. yAxis: [
  479. {
  480. name: '使用率(%)',
  481. nameLocation: 'middle',
  482. nameTextStyle: { padding: [3, 4, 50, 6] },
  483. splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
  484. axisLine: { show: false },
  485. axisTick: { show: false },
  486. axisLabel: { color: state.charts.color, formatter: '{value}' },
  487. }
  488. ],
  489. series: [
  490. {
  491. name: '使用率',
  492. type: 'line',
  493. smooth: true,
  494. showSymbol: true,
  495. data: [],
  496. itemStyle: { color: '#91cc75' }
  497. }
  498. ],
  499. };
  500. (<any>global.homeCharThree).setOption(option);
  501. (<any>state.myCharts).push(global.homeCharThree);
  502. };
  503. // 批量设置 echarts resize
  504. const initEchartsResizeFun = () => {
  505. nextTick(() => {
  506. for (let i = 0; i < state.myCharts.length; i++) {
  507. setTimeout(() => {
  508. (<any>state.myCharts[i]).resize();
  509. }, i * 1000);
  510. }
  511. });
  512. };
  513. // 批量设置 echarts resize
  514. const initEchartsResize = () => {
  515. window.addEventListener('resize', initEchartsResizeFun);
  516. };
  517. let scrollTimer: number | null = null
  518. const scrollSpeed = 1 // px per frame
  519. const scrollStep = () => {
  520. scrollOffset.value += scrollSpeed
  521. const scrollInner = document.querySelector('.scroll-inner')
  522. const scrollBody = document.querySelector('.scroll-body')
  523. if (scrollInner && scrollBody) {
  524. const maxScroll = scrollInner.scrollHeight - scrollBody.clientHeight
  525. if (scrollOffset.value >= maxScroll) {
  526. scrollOffset.value = 0 // 重置回顶部
  527. }
  528. }
  529. }
  530. const startScroll = () => {
  531. if (!scrollTimer) {
  532. scrollTimer = window.setInterval(scrollStep, 20) // 控制速度
  533. }
  534. }
  535. const stopScroll = () => {
  536. if (scrollTimer) {
  537. clearInterval(scrollTimer)
  538. scrollTimer = null
  539. }
  540. }
  541. const getList = async () => {
  542. const res = await GetList({
  543. page: 1,
  544. page_size: 20,
  545. })
  546. if (res.code === 2000) {
  547. labelType.value = res.data
  548. }
  549. }
  550. // 页面加载时
  551. onMounted(() => {
  552. fetchApiData();
  553. initEchartsResize();
  554. initTime(); // 初始化时间显示
  555. // initHeroScroll(); // 添加英雄数据轮播初始化
  556. getList();
  557. // 从本地存储加载面板设置
  558. const savedSettings = localStorage.getItem('dashboardPanelSettings');
  559. if (savedSettings) {
  560. state.panelSettings = JSON.parse(savedSettings);
  561. }
  562. nextTick(() => {
  563. });
  564. });
  565. // 由于页面缓存原因,keep-alive
  566. onActivated(() => {
  567. initEchartsResizeFun();
  568. });
  569. onUnmounted(() => {
  570. // if (intervalId) clearInterval(intervalId)
  571. // stopScroll();
  572. });
  573. // 监听 vuex 中的 tagsview 开启全屏变化,重新 resize 图表,防止不出现/大小不变等
  574. watch(
  575. () => isTagsViewCurrenFull.value,
  576. () => {
  577. initEchartsResizeFun();
  578. }
  579. );
  580. // 监听 vuex 中是否开启深色主题
  581. watch(
  582. () => themeConfig.value.isIsDark,
  583. (isIsDark) => {
  584. nextTick(() => {
  585. state.charts.theme = isIsDark ? 'dark' : '';
  586. state.charts.bgColor = isIsDark ? 'transparent' : '';
  587. state.charts.color = isIsDark ? '#dadada' : '#303133';
  588. setTimeout(() => {
  589. initLineChart();
  590. }, 500);
  591. setTimeout(() => {
  592. initPieChart();
  593. }, 700);
  594. setTimeout(() => {
  595. initBarChart();
  596. }, 1000);
  597. });
  598. },
  599. {
  600. deep: true,
  601. immediate: true,
  602. }
  603. );
  604. /* const userList = ref([]) */
  605. // 获取API数据
  606. const fetchApiData = async () => {
  607. try {
  608. const res = await api.GetOverviewList();
  609. if (res.code === 2000) {
  610. // state.apiData = res.data;
  611. // updateHomeData(res.data);
  612. state.devicedata = res.data;
  613. updateZbCharts(res.data);
  614. }
  615. const drres = await api.GetDeviceRankList();
  616. if (res.code === 2000) {
  617. state.teamRankings = [...drres.data.borrow_count_ranking].sort(
  618. (a, b) => b.borrow_count - a.borrow_count
  619. );
  620. state.heroData = [...drres.data.borrow_duration_ranking].sort((a, b) => Number(b.total_duration) - Number(a.total_duration))
  621. console.log("state.heroData:::", state.heroData)
  622. }
  623. const ulres = await api.GetUtilizationList();
  624. if (ulres.code === 2000) {
  625. //clos
  626. console.log("ulres:::", ulres)
  627. updateMonthlyUtilizationChart(ulres.data.trends)
  628. }
  629. const btres = await api.GetBorrowTrends();
  630. if (btres.code === 2000) {
  631. //clos
  632. updateBorrowTrendChart(btres.data.trends)
  633. }
  634. const acres = await api.GetActiveUsers();
  635. if (acres.code === 2000) {
  636. //clos
  637. /* updateActiveUserChart(acres.data.active_users) */
  638. state.userList = acres.data.active_users
  639. }
  640. } catch (error) {
  641. console.error('获取统计数据失败:', error);
  642. }
  643. };
  644. const handleQuickRange = (range: string) => {
  645. const today = dayjs();
  646. if (range === 'day') {
  647. dateRange.value = [today.format('YYYY-MM-DD'), today.format('YYYY-MM-DD')];
  648. } else if (range === 'month') {
  649. dateRange.value = [today.startOf('month').format('YYYY-MM-DD'), today.endOf('month').format('YYYY-MM-DD')];
  650. } else if (range === 'year') {
  651. dateRange.value = [today.startOf('year').format('YYYY-MM-DD'), today.endOf('year').format('YYYY-MM-DD')];
  652. }
  653. fetchRankings();
  654. };
  655. const fetchRankings = async () => {
  656. try {
  657. const params = {
  658. type: selectedType.value,
  659. start_date: dateRange.value?.[0],
  660. end_date: dateRange.value?.[1],
  661. tag_id:deviceTagId.value
  662. };
  663. const res = await api.GetBorrowRanking(params);
  664. if (res.code === 2000) {
  665. state.teamRankings = [...res.data.borrow_count_ranking].sort((a, b) => b.borrow_count - a.borrow_count);
  666. }
  667. } catch (err) {
  668. console.error('获取设备排名失败:', err);
  669. }
  670. };
  671. //更新活跃用户数据
  672. const updateActiveUserChart = (userData: Array<{ user_name: string; total_quantity: number }>) => {
  673. const echart2 = echarts.init(document.getElementById('echart2'));
  674. if (!echart2) return;
  675. const option = {
  676. tooltip: {
  677. trigger: 'item'
  678. },
  679. legend: {
  680. top: '5%',
  681. left: 'center',
  682. textStyle: {
  683. color: '#fff'
  684. }
  685. },
  686. series: [
  687. {
  688. name: '借用总量',
  689. type: 'pie',
  690. radius: ['40%', '70%'],
  691. avoidLabelOverlap: false,
  692. itemStyle: {
  693. borderRadius: 10,
  694. borderColor: '#fff',
  695. borderWidth: 2
  696. },
  697. label: {
  698. show: true,
  699. formatter: '{b}: {c}件'
  700. },
  701. data: userData.map(user => ({
  702. value: user.total_quantity,
  703. name: user.user_name
  704. }))
  705. }
  706. ]
  707. };
  708. echart2.setOption(option);
  709. };
  710. const groupByMonth = (data: Array<{ date: string; borrow_count: number; total_duration: number }>) => {
  711. const map = new Map<string, { borrow_count: number; total_duration: number }>()
  712. data.forEach(item => {
  713. const month = dayjs(item.date).format('YYYY-MM')
  714. if (!map.has(month)) {
  715. map.set(month, { borrow_count: 0, total_duration: 0 })
  716. }
  717. const current = map.get(month)!
  718. current.borrow_count += item.borrow_count
  719. current.total_duration += item.total_duration
  720. })
  721. return Array.from(map.entries()).map(([month, values]) => ({
  722. date: month, // 保持字段名为 date,兼容原图表逻辑
  723. borrow_count: values.borrow_count,
  724. total_duration: values.total_duration
  725. }))
  726. }
  727. // 更新首页数据
  728. const updateHomeData = (data: ApiData) => {
  729. // 更新顶部卡片数据
  730. state.homeOne[0].num1 = data.total_applications.toString();
  731. state.homeOne[1].num1 = data.total_positions.toString();
  732. state.homeOne[2].num1 = data.total_questions.toString();
  733. state.homeOne[3].num1 = data.total_reports.toString();
  734. // 更新折线图数据
  735. updateLineChart(data.application_trend);
  736. };
  737. //更新整体借用趋势
  738. const updateBorrowTrendChart = (trendData: Array<{ date: string; borrow_count: number; total_duration: number }>) => {
  739. const monthlyData = groupByMonth(trendData)
  740. const echart3 = echarts.init(document.getElementById('echart3'));
  741. if (!echart3) return;
  742. const option = {
  743. tooltip: {
  744. trigger: 'axis',
  745. axisPointer: {
  746. lineStyle: { color: '#57617B' }
  747. }
  748. },
  749. legend: {
  750. data: ['借用次数', '借用时长'],
  751. top: '0',
  752. textStyle: { color: '#fff' },
  753. itemGap: 20
  754. },
  755. grid: {
  756. left: '0',
  757. right: '20',
  758. top: '10',
  759. bottom: '20',
  760. containLabel: true
  761. },
  762. xAxis: [{
  763. type: 'category',
  764. boundaryGap: false,
  765. axisLabel: {
  766. show: true,
  767. textStyle: {
  768. color: 'rgba(255,255,255,1)',
  769. fontSize: 11
  770. }
  771. },
  772. axisLine: {
  773. lineStyle: { color: 'rgba(255,255,255,.1)' }
  774. },
  775. // data: trendData.map(item => item.date.substring(5))
  776. data: monthlyData.map(item => item.date)
  777. }],
  778. yAxis: [{
  779. axisLabel: {
  780. show: true,
  781. textStyle: { color: 'rgba(255,255,255,.6)' }
  782. },
  783. axisLine: {
  784. lineStyle: { color: 'rgba(255,255,255,.1)' }
  785. },
  786. splitLine: {
  787. lineStyle: { color: 'rgba(255,255,255,.1)' }
  788. }
  789. }],
  790. series: [
  791. {
  792. name: '借用次数',
  793. type: 'line',
  794. smooth: true,
  795. // data: trendData.map(item => item.borrow_count)
  796. data: monthlyData.map(item => item.borrow_count)
  797. },
  798. {
  799. name: '借用时长',
  800. type: 'line',
  801. smooth: true,
  802. // data: trendData.map(item => item.total_duration)
  803. data: monthlyData.map(item => item.total_duration)
  804. }
  805. ]
  806. };
  807. echart3.setOption(option);
  808. };
  809. //更新利用趋势图
  810. const updateMonthlyUtilizationChart = (monthlyData: Array<{ month: string; rate: number }>) => {
  811. const echart5 = echarts.init(document.getElementById('echart5'));
  812. if (!echart5) return;
  813. const option = {
  814. xAxis: {
  815. type: 'category',
  816. data: monthlyData.map(item => item.date),
  817. axisLabel: { color: '#fff' }
  818. },
  819. yAxis: {
  820. type: 'value',
  821. axisLabel: { color: '#fff' }
  822. },
  823. series: [
  824. {
  825. name: '月度利用率',
  826. type: 'line', // 或 'bar',看你想要什么样的图
  827. data: monthlyData.map(item => item.utilization_rate),
  828. smooth: true,
  829. itemStyle: { color: '#91cc75' }
  830. }
  831. ],
  832. tooltip: {
  833. trigger: 'axis',
  834. axisPointer: { type: 'line' }
  835. },
  836. legend: {
  837. data: ['月度利用率'],
  838. textStyle: { color: '#fff' }
  839. },
  840. grid: {
  841. left: '3%',
  842. right: '4%',
  843. bottom: '3%',
  844. containLabel: true
  845. }
  846. };
  847. echart5.setOption(option);
  848. };
  849. //更新设备信息
  850. const updateZbCharts = (devicedata: {
  851. total_devices: number;
  852. borrowed_devices: number;
  853. available_devices: number;
  854. today_borrowed: number;
  855. today_returned: number;
  856. total_borrows: number;
  857. }) => {
  858. // 移除图表初始化代码
  859. // 不再需要图表相关代码
  860. };
  861. // 更新折线图数据
  862. const updateLineChart = (trendData: Array<{ date: string; count: number }>) => {
  863. if (!global.homeChartOne) return;
  864. const option = {
  865. xAxis: {
  866. data: trendData.map((item: { date: string; count: number }) => item.date.substring(5)), // 只显示月-日
  867. },
  868. series: [
  869. {
  870. name: '申请数量',
  871. data: trendData.map((item: { date: string; count: number }) => item.count),
  872. }
  873. ]
  874. };
  875. (<any>global.homeChartOne).setOption(option);
  876. };
  877. // 添加卡片点击跳转功能
  878. const handleCardClick = (index: number) => {
  879. const routes = [
  880. '/JobApplication/list/index', // 申请总数
  881. '/position/list', // 职位总数
  882. '/questionBank/list', // 问题总数
  883. '/JobApplication/list/index' // 报告总数
  884. ];
  885. if (index >= 0 && index < routes.length) {
  886. router.push(routes[index]);
  887. }
  888. };
  889. // 初始化时间显示
  890. const initTime = () => {
  891. const showTime = () => {
  892. const dt = new Date();
  893. const y = dt.getFullYear();
  894. const mt = dt.getMonth() + 1;
  895. const day = dt.getDate();
  896. const h = dt.getHours();
  897. const m = dt.getMinutes();
  898. const s = dt.getSeconds();
  899. document.getElementById("showTime")!.innerHTML =
  900. y + "年" + mt + "月" + day + "日" + h + "时" + m + "分" + s + "秒";
  901. };
  902. showTime();
  903. setInterval(showTime, 1000);
  904. };
  905. //
  906. /* function echarts_3() {
  907. // 基于准备好的dom,初始化echarts实例
  908. var myChart = echarts.init(document.getElementById('echart3'));
  909. option3
  910. // 使用刚指定的配置项和数据显示图表。
  911. myChart.setOption(option);
  912. window.addEventListener("resize", function() {
  913. myChart.resize();
  914. });
  915. } */
  916. // 初始化英雄数据轮播
  917. const initHeroScroll = () => {
  918. const wrap = document.querySelector('.wrap') as HTMLElement;
  919. if (!wrap) return;
  920. const ul = wrap.querySelector('ul') as HTMLElement;
  921. if (!ul) return;
  922. // 克隆列表项用于无缝滚动
  923. const cloneItems = ul.innerHTML;
  924. ul.innerHTML = cloneItems + cloneItems;
  925. let scrollHeight = 0;
  926. const itemHeight = 32; // 每个列表项的高度
  927. const totalItems = state.heroData.length;
  928. const scroll = () => {
  929. scrollHeight++;
  930. if (scrollHeight >= totalItems * itemHeight) {
  931. scrollHeight = 0;
  932. ul.style.transition = 'none';
  933. ul.style.transform = `translateY(0)`;
  934. // 强制回流
  935. ul.offsetHeight;
  936. ul.style.transition = 'transform 0.5s linear';
  937. } else {
  938. ul.style.transform = `translateY(-${scrollHeight}px)`;
  939. }
  940. };
  941. // 每3秒滚动一次
  942. setInterval(() => {
  943. scroll();
  944. }, 3000);
  945. };
  946. const settingsDialogVisible = computed(() => state.settingsDialogVisible);
  947. const panelSettings = computed(() => state.panelSettings);
  948. return {
  949. homeLineRef,
  950. homePieRef,
  951. homeBarRef,
  952. selectedType,
  953. dateRange,
  954. labelType,
  955. deviceTagId,
  956. quickRange,
  957. handleCardClick,
  958. handleQuickRange,
  959. fetchRankings,
  960. scrollOffset,
  961. devicedata: computed(() => state.devicedata),
  962. heroData: computed(() => state.heroData),
  963. userList: computed(() => state.userList),
  964. teamRankings: computed(() => state.teamRankings),
  965. settingsDialogVisible,
  966. panelSettings,
  967. isFullscreen,
  968. toggleFullScreen,
  969. // 打开设置弹窗
  970. openSettings: () => {
  971. state.settingsDialogVisible = true;
  972. },
  973. // 保存设置
  974. saveSettings: () => {
  975. state.settingsDialogVisible = false;
  976. // 可以在这里添加保存到本地存储的逻辑
  977. localStorage.setItem('dashboardPanelSettings', JSON.stringify(state.panelSettings));
  978. },
  979. };
  980. },
  981. });
  982. </script>
  983. <style scoped lang="scss">
  984. .scroll-body {
  985. height: 300px;
  986. overflow: hidden;
  987. position: relative;
  988. }
  989. .scroll-inner {
  990. transition: transform 0.2s linear;
  991. }
  992. .scroll-table-wrapper {
  993. height: 300px;
  994. overflow: hidden;
  995. position: relative;
  996. }
  997. .scroll-table {
  998. transition: transform 0.5s linear;
  999. }
  1000. .transparent-radio ::v-deep(.el-radio-button__inner) {
  1001. background-color: transparent;
  1002. border-color: transparent;
  1003. color: #ede6e6;
  1004. box-shadow: none;
  1005. }
  1006. .transparent-radio ::v-deep(.el-radio-button.is-active .el-radio-button__inner) {
  1007. background-color: rgba(64, 158, 255, 0.1);
  1008. border-color: #409EFF;
  1009. color: #409EFF;
  1010. }
  1011. /* Select组件样式 */
  1012. :deep(.el-select__wrapper) {
  1013. color: #fff !important;
  1014. background-color: transparent !important;
  1015. border-color: transparent !important;
  1016. .el-input__wrapper {
  1017. background-color: transparent !important;
  1018. box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1) !important;
  1019. &:hover {
  1020. box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2) !important;
  1021. }
  1022. &.is-focus {
  1023. box-shadow: 0 0 0 1px #409EFF !important;
  1024. }
  1025. }
  1026. .el-input__inner {
  1027. color: #fff !important;
  1028. &::placeholder {
  1029. color: rgba(255, 255, 255, 0.5) !important;
  1030. }
  1031. }
  1032. }
  1033. /* Select下拉框样式 */
  1034. :deep(.el-select__popper) {
  1035. background-color: #001d6e !important;
  1036. border: 1px solid rgba(255, 255, 255, 0.1) !important;
  1037. .el-select-dropdown__item {
  1038. color: #fff !important;
  1039. &.hover, &:hover {
  1040. background-color: rgba(64, 158, 255, 0.1) !important;
  1041. }
  1042. &.selected {
  1043. background-color: rgba(64, 158, 255, 0.2) !important;
  1044. color: #409EFF !important;
  1045. }
  1046. }
  1047. }
  1048. :deep(.el-date-editor) {
  1049. color: #fff !important;
  1050. background-color: transparent !important;
  1051. border-color: transparent !important;
  1052. }
  1053. $homeNavLengh: 8;
  1054. .home-container {
  1055. overflow: hidden;
  1056. height: 100%;
  1057. &:fullscreen {
  1058. background: #000d4a;
  1059. height: 100vh;
  1060. width: 100vw;
  1061. .lol-data-section {
  1062. height: 100vh;
  1063. margin: 0;
  1064. .el-col {
  1065. padding: 0 !important;
  1066. }
  1067. .lol-container {
  1068. height: 100vh;
  1069. border-radius: 0;
  1070. }
  1071. }
  1072. }
  1073. .home-card-one,
  1074. .home-card-two,
  1075. .home-card-three {
  1076. .home-card-one-title {
  1077. padding: 20px 0px;
  1078. color: #808080;
  1079. font-size: 14px;
  1080. letter-spacing: 3px;
  1081. }
  1082. .home-card-item {
  1083. width: 100%;
  1084. height: 130px;
  1085. border-radius: 16px;
  1086. transition: all ease 0.3s;
  1087. padding: 20px;
  1088. overflow: hidden;
  1089. background: var(--el-color-white);
  1090. color: var(--el-text-color-primary);
  1091. border: 1px solid var(--next-border-color-light);
  1092. &:hover {
  1093. box-shadow: 0 2px 12px var(--next-color-dark-hover);
  1094. transition: all ease 0.3s;
  1095. }
  1096. &-icon {
  1097. width: 70px;
  1098. height: 70px;
  1099. border-radius: 100%;
  1100. flex-shrink: 1;
  1101. i {
  1102. color: var(--el-text-color-placeholder);
  1103. }
  1104. }
  1105. &-title {
  1106. font-size: 15px;
  1107. font-weight: bold;
  1108. height: 30px;
  1109. }
  1110. }
  1111. }
  1112. .home-card-one {
  1113. @for $i from 0 through 3 {
  1114. .home-one-animation#{$i} {
  1115. opacity: 0;
  1116. animation-name: error-num;
  1117. animation-duration: 0.5s;
  1118. animation-fill-mode: forwards;
  1119. animation-delay: calc($i/10) + s;
  1120. }
  1121. }
  1122. }
  1123. .home-card-two,
  1124. .home-card-three {
  1125. .home-card-item {
  1126. height: 400px;
  1127. width: 100%;
  1128. overflow: hidden;
  1129. .home-monitor {
  1130. height: 100%;
  1131. .flex-warp-item {
  1132. width: 25%;
  1133. height: 111px;
  1134. display: flex;
  1135. .flex-warp-item-box {
  1136. margin: auto;
  1137. text-align: center;
  1138. color: var(--el-text-color-primary);
  1139. display: flex;
  1140. border-radius: 5px;
  1141. background: var(--next-bg-color);
  1142. cursor: pointer;
  1143. transition: all 0.3s ease;
  1144. &:hover {
  1145. background: var(--el-color-primary-light-9);
  1146. transition: all 0.3s ease;
  1147. }
  1148. }
  1149. @for $i from 0 through $homeNavLengh {
  1150. .home-animation#{$i} {
  1151. opacity: 0;
  1152. animation-name: error-num;
  1153. animation-duration: 0.5s;
  1154. animation-fill-mode: forwards;
  1155. animation-delay: calc($i/10) + s;
  1156. }
  1157. }
  1158. }
  1159. }
  1160. }
  1161. }
  1162. .home-right {
  1163. .home-card-item {
  1164. height: 500px;
  1165. border-radius: 16px;
  1166. transition: all ease 0.3s;
  1167. padding: 20px;
  1168. overflow: hidden;
  1169. background: var(--el-color-white);
  1170. color: var(--el-text-color-primary);
  1171. border: 1px solid var(--next-border-color-light);
  1172. .home-card-item-content {
  1173. display: flex;
  1174. }
  1175. ::v-deep(.el-collapse-item__header) {
  1176. border: none;
  1177. }
  1178. ::v-deep(.el-collapse-item__wrap) {
  1179. border: none;
  1180. }
  1181. ::v-deep(.el-collapse-item__content) {
  1182. padding: 0;
  1183. }
  1184. &-title {
  1185. font-size: 15px;
  1186. font-weight: bold;
  1187. height: 30px;
  1188. border-bottom: 1px solid var(--next-border-color-light);
  1189. padding-bottom: 10px;
  1190. margin-bottom: 10px;
  1191. }
  1192. &:hover {
  1193. box-shadow: 0 2px 12px var(--next-color-dark-hover);
  1194. transition: all ease 0.3s;
  1195. }
  1196. }
  1197. }
  1198. // 响应式处理
  1199. @media screen and (max-width: 1200px) {
  1200. .el-col-18 {
  1201. width: 100%;
  1202. }
  1203. .el-col-6 {
  1204. width: 100%;
  1205. margin-top: 15px;
  1206. }
  1207. .home-right .home-card-item {
  1208. height: auto;
  1209. min-height: 300px;
  1210. }
  1211. }
  1212. .todo-list {
  1213. padding: 10px 20px;
  1214. margin-top: 15px;
  1215. margin-left: 50px;
  1216. .todo-item {
  1217. display: flex;
  1218. justify-content: space-between;
  1219. align-items: center;
  1220. padding: 8px 0;
  1221. border-bottom: 1px solid var(--next-border-color-light);
  1222. .todo-item-left {
  1223. display: flex;
  1224. flex-direction: column;
  1225. align-items: flex-start;
  1226. .todo-tag {
  1227. margin-right: 10px;
  1228. }
  1229. }
  1230. &:last-child {
  1231. border-bottom: none;
  1232. }
  1233. .todo-date {
  1234. color: var(--el-text-color-secondary);
  1235. font-size: 13px;
  1236. }
  1237. :deep(.el-checkbox__label) {
  1238. color: var(--el-text-color-primary);
  1239. }
  1240. }
  1241. }
  1242. .score-list {
  1243. padding: 10px 0;
  1244. .score-item {
  1245. display: flex;
  1246. justify-content: space-between;
  1247. align-items: center;
  1248. padding: 8px 15px;
  1249. margin: 5px 0;
  1250. background: var(--el-fill-color-light);
  1251. border-radius: 4px;
  1252. .score-name {
  1253. font-size: 14px;
  1254. color: var(--el-text-color-primary);
  1255. }
  1256. .score-value {
  1257. font-size: 14px;
  1258. color: var(--el-color-primary);
  1259. font-weight: 500;
  1260. }
  1261. }
  1262. }
  1263. // 添加英雄联盟数据可视化样式
  1264. .lol-data-section {
  1265. .lol-container {
  1266. border-radius: 16px;
  1267. color: #fff;
  1268. background: #000d4a; // url(/@/assets/img/bg.png) center top
  1269. .head {
  1270. height: 105px;
  1271. background: url(/@/assets/img/head_bg.png) no-repeat center center;
  1272. text-align: center;
  1273. position: relative;
  1274. h1 {
  1275. font-size: 24px;
  1276. color: #fff;
  1277. line-height: 90px;
  1278. text-align: center;
  1279. }
  1280. .weather {
  1281. position: absolute;
  1282. right: 20px;
  1283. top: 0;
  1284. line-height: 85px;
  1285. display: flex;
  1286. align-items: center;
  1287. gap: 15px;
  1288. .fullscreen-btn {
  1289. color: #fff;
  1290. font-size: 20px;
  1291. padding: 0;
  1292. margin-left: 10px;
  1293. &:hover {
  1294. color: #409EFF;
  1295. }
  1296. }
  1297. }
  1298. }
  1299. .mainbox {
  1300. height: calc(100vh - 105px);
  1301. padding: 10px 20px;
  1302. display: flex;
  1303. flex-direction: column;
  1304. .boxall {
  1305. border: 1px solid rgba(25, 186, 139, 0.17);
  1306. padding: 15px;
  1307. background: rgba(255, 255, 255, 0.04);
  1308. margin-bottom: 15px;
  1309. border-radius: 8px;
  1310. // 添加表格透明样式
  1311. :deep(.el-table) {
  1312. background-color: transparent;
  1313. &::before {
  1314. display: none;
  1315. }
  1316. tr {
  1317. background-color: transparent;
  1318. }
  1319. th {
  1320. background-color: transparent;
  1321. border-bottom-color: rgba(255, 255, 255, 0.1);
  1322. color: #fff;
  1323. }
  1324. td {
  1325. background-color: transparent;
  1326. border-bottom-color: rgba(255, 255, 255, 0.1);
  1327. color: #fff;
  1328. }
  1329. .el-table__cell {
  1330. background-color: transparent;
  1331. }
  1332. // 添加表头背景透明
  1333. .el-table__header {
  1334. background-color: transparent;
  1335. tr {
  1336. background-color: transparent;
  1337. }
  1338. th.el-table__cell {
  1339. background-color: transparent !important;
  1340. }
  1341. }
  1342. // 添加表体背景透明
  1343. .el-table__body {
  1344. background-color: transparent;
  1345. tr {
  1346. background-color: transparent !important;
  1347. &:hover>td.el-table__cell {
  1348. background-color: rgba(255, 255, 255, 0.1) !important;
  1349. }
  1350. }
  1351. }
  1352. }
  1353. .alltitle {
  1354. font-size: 16px;
  1355. color: #fff;
  1356. padding-left: 12px;
  1357. margin-bottom: 10px;
  1358. position: relative;
  1359. }
  1360. .alltitle:before {
  1361. width: 5px;
  1362. height: 20px;
  1363. top: 2px;
  1364. position: absolute;
  1365. content: "";
  1366. background: #49bcf7;
  1367. border-radius: 20px;
  1368. left: 0;
  1369. }
  1370. }
  1371. ul.clearfix {
  1372. display: flex;
  1373. justify-content: space-between;
  1374. margin: 0;
  1375. padding: 0;
  1376. list-style: none;
  1377. height: 100%;
  1378. li {
  1379. width: 32%;
  1380. display: flex;
  1381. flex-direction: column;
  1382. .boxall {
  1383. flex: 1;
  1384. display: flex;
  1385. flex-direction: column;
  1386. .navboxall {
  1387. flex: 1;
  1388. display: flex;
  1389. flex-direction: column;
  1390. }
  1391. }
  1392. }
  1393. .wrap {
  1394. height: 200px;
  1395. overflow: auto;
  1396. ul {
  1397. list-style: none;
  1398. padding: 0;
  1399. margin: 0;
  1400. transition: transform 0.5s linear;
  1401. li {
  1402. line-height: 32px;
  1403. font-size: 14px;
  1404. p {
  1405. display: flex;
  1406. justify-content: space-between;
  1407. margin: 0;
  1408. padding: 0 10px;
  1409. color: #fff;
  1410. span {
  1411. flex: 1;
  1412. text-align: center;
  1413. &:first-child {
  1414. text-align: left;
  1415. }
  1416. }
  1417. }
  1418. &:hover {
  1419. background: rgba(255, 255, 255, 0.1);
  1420. }
  1421. }
  1422. }
  1423. }
  1424. .wrap::-webkit-scrollbar {
  1425. display: none;
  1426. /* 隐藏滚动条 */
  1427. }
  1428. .wraptit {
  1429. display: flex;
  1430. justify-content: space-between;
  1431. padding: 0 10px;
  1432. margin-bottom: 10px;
  1433. color: #fff;
  1434. font-weight: bold;
  1435. span {
  1436. flex: 1;
  1437. text-align: center;
  1438. &:first-child {
  1439. text-align: center;
  1440. }
  1441. }
  1442. }
  1443. .wrap {
  1444. span {
  1445. flex: 1;
  1446. text-align: center;
  1447. &:first-child {
  1448. text-align: center;
  1449. }
  1450. }
  1451. }
  1452. .data-overview {
  1453. display: grid;
  1454. grid-template-columns: repeat(3, 1fr);
  1455. gap: 15px;
  1456. padding: 10px;
  1457. height: 100%;
  1458. .data-card {
  1459. background: rgba(255, 255, 255, 0.1);
  1460. border-radius: 8px;
  1461. position: relative;
  1462. overflow: hidden;
  1463. transition: all 0.3s ease;
  1464. height: 120px;
  1465. border: none;
  1466. &:hover {
  1467. transform: translateY(-5px);
  1468. box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  1469. background: rgba(255, 255, 255, 0.15);
  1470. }
  1471. .card-content {
  1472. display: flex;
  1473. flex-direction: column;
  1474. align-items: flex-start;
  1475. position: relative;
  1476. z-index: 1;
  1477. height: 100%;
  1478. padding: 15px;
  1479. .card-title {
  1480. font-size: 16px;
  1481. color: rgba(255, 255, 255, 0.9);
  1482. margin-bottom: 20px;
  1483. }
  1484. .card-value {
  1485. font-size: 48px;
  1486. font-weight: bold;
  1487. color: #fff;
  1488. line-height: 1;
  1489. margin-bottom: 10px;
  1490. }
  1491. .card-unit {
  1492. font-size: 16px;
  1493. color: rgba(255, 255, 255, 0.7);
  1494. margin-left: 5px;
  1495. }
  1496. .card-subtitle {
  1497. font-size: 14px;
  1498. color: rgba(255, 255, 255, 0.6);
  1499. margin-top: 5px;
  1500. }
  1501. }
  1502. .chart-container {
  1503. height: 60px;
  1504. margin-top: 10px;
  1505. }
  1506. // 为不同卡片设置不同的背景色
  1507. &.total-devices {
  1508. background: #1e3799;
  1509. }
  1510. &.borrowed {
  1511. background: #ff7f50;
  1512. }
  1513. &.available {
  1514. background: #2ecc71;
  1515. }
  1516. &.today-borrowed {
  1517. background: #0984e3;
  1518. }
  1519. &.today-returned {
  1520. background: #9b59b6;
  1521. }
  1522. &.total-borrows {
  1523. background: #8e44ad;
  1524. }
  1525. }
  1526. }
  1527. // .zhibiao > div {
  1528. // width: 32%;
  1529. // margin-bottom: 10px;
  1530. // }
  1531. }
  1532. }
  1533. }
  1534. }
  1535. }
  1536. .panel-settings {
  1537. display: grid;
  1538. grid-template-columns: repeat(2, 1fr);
  1539. gap: 20px;
  1540. padding: 20px;
  1541. .el-checkbox {
  1542. margin-right: 0;
  1543. color: #606266;
  1544. }
  1545. }
  1546. </style>