index.vue 82 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963
  1. <template>
  2. <div class="home-container">
  3. <el-row :gutter="6" class="home-top-row">
  4. <el-col :span="24" class="box-col">
  5. <el-card class="box-card">
  6. <div slot="header" class="clearfix">
  7. <span>常用功能</span>
  8. <span style="float: right; padding: 3px 0">
  9. <i
  10. class="el-icon-edit"
  11. @click="handleEdit"
  12. style="cursor: pointer; margin-right: 16px; font-size: 18px"
  13. ></i>
  14. <i
  15. class="el-icon-setting"
  16. @click="handleAdd"
  17. style="cursor: pointer; font-size: 18px"
  18. ></i>
  19. </span>
  20. </div>
  21. <div class="card-content common-function-card">
  22. <vuedraggable v-model="commonFunctions" class="common-function">
  23. <div
  24. v-for="(item, index) in commonFunctions"
  25. :key="item.id"
  26. class="common-function-item"
  27. @click="handleDel(item, index)"
  28. :title="isDelFlag ? '点击删除' : ''"
  29. :class="isDelFlag ? 'div-del' : ''"
  30. >
  31. <!-- <div class="common-function-item-content"> -->
  32. <div class="common-function-item-icon">
  33. <!-- <i :class="item.icon || 'el-icon-s-opportunity'"></i> -->
  34. <img :src="getIconPath(item.pic)" alt="" />
  35. </div>
  36. <div class="common-function-item-name">{{ item.name }}</div>
  37. <!-- </div> -->
  38. </div>
  39. </vuedraggable>
  40. </div>
  41. </el-card>
  42. </el-col>
  43. </el-row>
  44. <el-row :gutter="6" class="mt-20 home-overview-row">
  45. <el-col :span="18" class="box-col">
  46. <el-card class="box-card">
  47. <div slot="header" class="clearfix">
  48. <span>数据概览</span>
  49. </div>
  50. <div class="card-content">
  51. <el-row :gutter="20" class="overview-container">
  52. <el-col :span="8">
  53. <div class="overview">
  54. <div class="overview-top">
  55. <div class="overview-top-img"
  56. ><img src="~@/assets/home/ceshi1.png" alt=""
  57. /></div>
  58. <div class="overview-top-content">
  59. <div class="overview-top-content-name">我的项目</div>
  60. <div class="overview-top-content-count">{{
  61. myProjectList['总数量'] || 0
  62. }}</div>
  63. </div>
  64. </div>
  65. <el-divider></el-divider>
  66. <div class="overview-bottom">
  67. <div
  68. v-for="(value, key) in myProjectList"
  69. :key="key"
  70. v-if="key !== '总数量' && value != 0"
  71. class="overview-bottom-item"
  72. @click="handleToPro(key)"
  73. >
  74. <div class="overview-bottom-item-name">{{ key }}</div>
  75. <div class="overview-bottom-item-count">{{
  76. value || 0
  77. }}</div>
  78. </div>
  79. </div>
  80. </div>
  81. </el-col>
  82. <el-col :span="8">
  83. <div class="overview">
  84. <div class="overview-top">
  85. <div class="overview-top-img"
  86. ><img src="~@/assets/home/ceshi.jpg" alt=""
  87. /></div>
  88. <div class="overview-top-content">
  89. <div class="overview-top-content-name">我的工单</div>
  90. <div class="overview-top-content-count">{{
  91. myWorkOrderList['总数量'] || 0
  92. }}</div>
  93. </div>
  94. </div>
  95. <el-divider></el-divider>
  96. <div class="overview-bottom">
  97. <div
  98. v-for="(value, key) in myWorkOrderList"
  99. :key="key"
  100. v-if="key !== '总数量' && value != 0"
  101. class="overview-bottom-item"
  102. @click="handleTo(key)"
  103. >
  104. <div class="overview-bottom-item-name">{{ key }}</div>
  105. <div class="overview-bottom-item-count">{{
  106. value || 0
  107. }}</div>
  108. </div>
  109. </div>
  110. </div>
  111. </el-col>
  112. <el-col :span="8">
  113. <div class="overview">
  114. <div class="overview-top">
  115. <div class="overview-top-img"
  116. ><img
  117. style="border-radius: 10px"
  118. src="~@/assets/home/ceshi2.jpg"
  119. alt=""
  120. /></div>
  121. <div class="overview-top-content">
  122. <div class="overview-top-content-name">我的任务</div>
  123. <div class="overview-top-content-count">{{
  124. myTaskList['总数量'] || 0
  125. }}</div>
  126. </div>
  127. </div>
  128. <el-divider></el-divider>
  129. <div class="overview-bottom">
  130. <div
  131. v-for="(value, key) in myTaskList"
  132. :key="key"
  133. v-if="key !== '总数量' && value != 0"
  134. class="overview-bottom-item"
  135. @click="handleTo(key)"
  136. >
  137. <div class="overview-bottom-item-name">{{ key }}</div>
  138. <div class="overview-bottom-item-count">{{
  139. value || 0
  140. }}</div>
  141. </div>
  142. </div>
  143. </div>
  144. </el-col>
  145. </el-row>
  146. </div>
  147. </el-card>
  148. </el-col>
  149. <el-col :span="6" class="box-col">
  150. <el-card class="box-card">
  151. <div slot="header" class="clearfix">
  152. <span>统一门户</span>
  153. </div>
  154. <div class="card-content portal-content">
  155. <div class="portal">
  156. <el-row :gutter="12">
  157. <el-col
  158. :span="8"
  159. v-for="item in portals"
  160. :key="item.name"
  161. class="portal-item"
  162. >
  163. <div class="portal-item-content" @click="openUrl(item)">
  164. <div class="portal-item-img"
  165. ><img :src="item.img" alt=""
  166. /></div>
  167. <div class="portal-item-name">{{ item.name }}</div>
  168. </div>
  169. </el-col>
  170. </el-row>
  171. </div>
  172. </div>
  173. </el-card>
  174. </el-col>
  175. </el-row>
  176. <el-row :gutter="6" class="mt-20 home-bottom-row">
  177. <el-col :span="12" class="box-col notification-col">
  178. <el-card class="box-card notification-card">
  179. <div class="card-content">
  180. <el-tabs v-model="activeTab" @tab-click="handleTabClick">
  181. <el-tab-pane label="通知公告" name="notice"></el-tab-pane>
  182. <!-- <el-tab-pane label="行业动态" name="industry"></el-tab-pane> -->
  183. <el-tab-pane label="系统通知" name="system"></el-tab-pane>
  184. </el-tabs>
  185. <div v-if="notifications.length > 0" class="notification-list">
  186. <div
  187. v-for="(item, index) in notifications"
  188. :key="index"
  189. class="notification-item"
  190. @click="handleNotificationsClick(item)"
  191. >
  192. <div class="notification-dot"></div>
  193. <div class="notification-content">
  194. <div class="notification-header">
  195. <div class="notification-title">{{ item.title }}</div>
  196. <div v-if="activeTab == 'notice'" class="notification-text">
  197. {{ item.content }}
  198. <!-- <div v-if="item.images && item.images.length > 0" class="notification-images">
  199. <div v-for="(img, imgIndex) in item.images" :key="imgIndex" class="image-url">{{ img }}</div>
  200. </div> -->
  201. </div>
  202. <div v-else class="notification-text">{{
  203. item.content
  204. }}</div>
  205. </div>
  206. <div class="notification-info"
  207. >{{ item.createUser }}
  208. <span style="margin-left: 20px">{{
  209. item.department
  210. }}</span></div
  211. >
  212. </div>
  213. <div class="notification-date">{{ item.createTime }}</div>
  214. </div>
  215. <div
  216. v-if="hasMore"
  217. class="notification-more"
  218. @click="loadMoreNotifications"
  219. >
  220. 更多......
  221. </div>
  222. </div>
  223. <div v-else class="no-notification">
  224. <el-empty description="暂无数据"></el-empty>
  225. </div>
  226. </div>
  227. </el-card>
  228. </el-col>
  229. <el-col :span="12" class="box-col calendar-col">
  230. <el-card class="box-card calendar-card">
  231. <div slot="header" class="clearfix">
  232. <span>工作日历</span>
  233. </div>
  234. <div class="card-content calendar-content">
  235. <div class="work-calendar">
  236. <div class="calendar-view-title">
  237. <span>{{ calendarViewTitle }}</span>
  238. <div class="calendar-title-tools">
  239. <el-radio-group
  240. v-model="selectedCalendarType"
  241. size="mini"
  242. class="calendar-type-switch"
  243. @change="handleCalendarTypeChange"
  244. >
  245. <el-radio-button
  246. v-for="item in calendarTypeOptions"
  247. :key="item.value"
  248. :label="item.value"
  249. >
  250. {{ item.shortLabel }}
  251. </el-radio-button>
  252. </el-radio-group>
  253. </div>
  254. </div>
  255. <div class="calendar-week">
  256. <span v-for="item in weekNames" :key="item">{{ item }}</span>
  257. </div>
  258. <div ref="calendarGrid" class="calendar-grid">
  259. <el-tooltip
  260. v-for="day in calendarCells"
  261. :key="day.key"
  262. :disabled="!day.date"
  263. placement="auto"
  264. effect="light"
  265. popper-class="calendar-day-tooltip"
  266. :popper-append-to-body="true"
  267. :popper-options="calendarTooltipPopperOptions"
  268. :manual="true"
  269. :value="activeCalendarTooltipKey === day.key"
  270. :open-delay="120"
  271. :hide-after="0"
  272. :enterable="false"
  273. >
  274. <div slot="content" class="calendar-tooltip-content">
  275. <div class="tooltip-head">
  276. <strong>{{ day.date }}</strong>
  277. <span v-if="isConflictDay(day)">冲突</span>
  278. </div>
  279. <div class="tooltip-row">
  280. <span>日期类型</span>
  281. <strong>{{ getDateTypeText(getDayDateType(day)) }}</strong>
  282. </div>
  283. <div class="tooltip-row">
  284. <span>时段</span>
  285. <strong>{{ getDayRangeText(getDayDetails(day)) }}</strong>
  286. </div>
  287. <div class="tooltip-row">
  288. <span>排班状态</span>
  289. <strong>{{ hasSchedule(day.date) ? '已排产' : '未排产/空闲' }}</strong>
  290. </div>
  291. <div class="tooltip-row">
  292. <span>临时调整</span>
  293. <strong>{{ isTempAdjustDay(day) ? '是' : '否' }}</strong>
  294. </div>
  295. <div class="tooltip-row">
  296. <span>冲突说明</span>
  297. <strong>{{ getDayConflictText(day) || '无' }}</strong>
  298. </div>
  299. <div class="tooltip-stats">
  300. <span>关联计划 {{ getDayPlanCount(day) }}</span>
  301. <span>明细 {{ getDayDetails(day).length }}</span>
  302. </div>
  303. </div>
  304. <div
  305. class="calendar-cell"
  306. :class="getCalendarDayClass(day)"
  307. @mouseenter="showCalendarTooltip(day)"
  308. @mouseleave="hideCalendarTooltip(day)"
  309. @click="day.date && openCalendarDayDrawer(day)"
  310. >
  311. <div v-if="day.date" class="day-head">
  312. <span>{{ day.dayText }}</span>
  313. <el-tag
  314. v-if="isHolidayDay(day)"
  315. size="mini"
  316. class="holiday-tag"
  317. >
  318. 节假日
  319. </el-tag>
  320. <el-tag
  321. v-else-if="isTempAdjustDay(day)"
  322. size="mini"
  323. class="temp-adjust-tag"
  324. >
  325. 临调
  326. </el-tag>
  327. <em v-if="day.isToday">今</em>
  328. </div>
  329. <div v-if="day.date" class="day-body">
  330. <span
  331. v-for="(item, index) in getDayPeriodList(day)"
  332. :key="`${day.date}-${index}`"
  333. class="period-pill"
  334. >
  335. {{ item }}
  336. </span>
  337. </div>
  338. <div v-if="day.date" class="day-foot">
  339. <span v-if="getDayStatusText(day)" class="plan-text">
  340. {{ getDayStatusText(day) }}
  341. </span>
  342. <span v-if="isConflictDay(day)" class="conflict-text">
  343. 冲突
  344. </span>
  345. </div>
  346. </div>
  347. </el-tooltip>
  348. </div>
  349. </div>
  350. </div>
  351. </el-card>
  352. </el-col>
  353. </el-row>
  354. <el-drawer
  355. :visible.sync="dayDrawerVisible"
  356. :title="dayDrawerTitle"
  357. size="460px"
  358. append-to-body
  359. custom-class="calendar-day-drawer"
  360. >
  361. <div class="drawer-body" v-if="currentDay.date">
  362. <div class="drawer-summary">
  363. <div class="summary-date">
  364. <span>{{ drawerDateMonth }}</span>
  365. <strong>{{ drawerDateDay }}</strong>
  366. </div>
  367. <div class="summary-info">
  368. <div class="summary-title">
  369. {{ getDateTypeText(getDayDateType(currentDay)) }}
  370. <el-tag
  371. size="mini"
  372. :type="currentDay.scheduleStatus ? 'success' : 'info'"
  373. >
  374. {{ currentDay.scheduleStatus ? '已排班' : '未排班' }}
  375. </el-tag>
  376. </div>
  377. <p>{{ getDayRangeText(currentDay.details) }}</p>
  378. <div class="summary-tags">
  379. <el-tag v-if="currentDay.isRest" size="mini" type="info">
  380. 休息/节假日
  381. </el-tag>
  382. <el-tag v-if="currentDay.isTempAdjust" size="mini">
  383. 临时调整
  384. </el-tag>
  385. <el-tag v-if="currentDay.isConflict" size="mini" type="danger">
  386. 存在冲突
  387. </el-tag>
  388. <el-tag v-if="currentDay.planCount" size="mini" type="warning">
  389. {{ currentDay.planCount }} 个计划
  390. </el-tag>
  391. </div>
  392. </div>
  393. </div>
  394. <div class="drawer-section">
  395. <div class="drawer-title">
  396. <span>时段明细</span>
  397. <em>{{ currentDay.details.length }} 条</em>
  398. </div>
  399. <div v-if="currentDay.details.length" class="detail-timeline">
  400. <div
  401. v-for="(detail, index) in currentDay.details"
  402. :key="detail.id || index"
  403. class="detail-item"
  404. >
  405. <div class="detail-time">
  406. <strong>{{ getDetailTimeText(detail) }}</strong>
  407. </div>
  408. <div class="detail-content">
  409. <div>
  410. <strong>{{ getDetailTitle(detail) }}</strong>
  411. <span>{{ getDetailTimeText(detail) }}</span>
  412. </div>
  413. <el-tag
  414. size="mini"
  415. :type="isCalendarDetailScheduled(detail) ? 'success' : 'info'"
  416. >
  417. {{ isCalendarDetailScheduled(detail) ? '已排班' : '未排班' }}
  418. </el-tag>
  419. </div>
  420. </div>
  421. </div>
  422. <div v-else class="drawer-empty">暂无时段明细</div>
  423. </div>
  424. <div class="drawer-section">
  425. <div class="drawer-title">
  426. <span>{{ relatedSectionTitle }}</span>
  427. <em>{{ getCurrentDayRelatedPlans().length }} 条</em>
  428. </div>
  429. <div v-if="getCurrentDayRelatedPlans().length" class="plan-list">
  430. <el-tooltip
  431. v-for="(plan, index) in getCurrentDayRelatedPlans()"
  432. :key="getPlanUniqueKey(plan, index)"
  433. placement="top"
  434. effect="light"
  435. >
  436. <div slot="content" class="plan-tooltip">
  437. <template v-if="isTeamQueueDrawer">
  438. <div>排班名称:{{ getRelatedQueueName(plan) }}</div>
  439. <div>人员:{{ getRelatedQueueUserName(plan) || '-' }}</div>
  440. </template>
  441. <template v-else>
  442. <div>
  443. {{ isEquipmentDrawer ? '设备编码' : '计划编号' }}:{{
  444. getRelatedPlanCode(plan, index)
  445. }}
  446. </div>
  447. <div>
  448. {{ isEquipmentDrawer ? '设备名称' : '计划名称' }}:{{
  449. getRelatedPlanName(plan)
  450. }}
  451. </div>
  452. <div v-if="!isEquipmentDrawer">
  453. 订单编号:{{ plan.orderNo || '-' }}
  454. </div>
  455. <div v-if="!isEquipmentDrawer">
  456. 计划数量:{{ getPlanQuantity(plan) || '-' }}
  457. </div>
  458. <div>开始时间:{{ getPlanStartTime(plan) || '-' }}</div>
  459. <div>结束时间:{{ getPlanEndTime(plan) || '-' }}</div>
  460. </template>
  461. </div>
  462. <div class="plan-item">
  463. <div v-if="isTeamQueueDrawer" class="plan-main">
  464. <div class="plan-topline">
  465. <strong>{{ getRelatedQueueName(plan) }}</strong>
  466. <span>排班</span>
  467. </div>
  468. <div class="plan-name">
  469. 人员:{{ getRelatedQueueUserName(plan) || '-' }}
  470. </div>
  471. </div>
  472. <div v-else class="plan-main">
  473. <div class="plan-topline">
  474. <strong>{{ getRelatedPlanCode(plan, index) }}</strong>
  475. <span v-if="!isEquipmentDrawer && plan.orderNo">
  476. {{ plan.orderNo }}
  477. </span>
  478. </div>
  479. <div class="plan-name">{{ getRelatedPlanName(plan) }}</div>
  480. <div class="plan-bottomline">
  481. <span class="plan-metrics">
  482. <em v-if="!isEquipmentDrawer && getPlanQuantity(plan)">
  483. 数量 {{ getPlanQuantity(plan) }}
  484. </em>
  485. <em v-if="getPlanTimeRange(plan)">
  486. {{ getPlanTimeRange(plan) }}
  487. </em>
  488. </span>
  489. </div>
  490. </div>
  491. </div>
  492. </el-tooltip>
  493. </div>
  494. <div v-else class="drawer-empty plan-empty">
  495. <i class="el-icon-box"></i>
  496. <span>暂无关联计划</span>
  497. </div>
  498. </div>
  499. </div>
  500. </el-drawer>
  501. <commonDialog
  502. ref="commonDialogRef"
  503. v-if="commonDialogFlag"
  504. :common-dialog-flag.sync="commonDialogFlag"
  505. @reload="getCommonFunctionsList"
  506. ></commonDialog>
  507. <noticeIssuanceDetail ref="noticeIssuanceDetailRef"></noticeIssuanceDetail>
  508. </div>
  509. </template>
  510. <script>
  511. import { notifyMessagePageAPI } from '@/api/bpm/task';
  512. import {
  513. userResourceDeleteAPI,
  514. userResourceListAPI,
  515. getList,
  516. getMyProjectTotal,
  517. getMyWorkOrderTotal,
  518. getMyTaskTotal
  519. } from '@/api/home';
  520. import { viewCalendar } from '@/api/productionScheduling/factoryCalendar';
  521. import { deepClone } from 'ele-admin/lib/utils/core';
  522. import { mapGetters } from 'vuex';
  523. import vuedraggable from 'vuedraggable';
  524. import commonDialog from '@/views/home/common-dialog.vue';
  525. import { noticeDocumentPageV1API } from '@/api/documents/noticeIssuance/index.js';
  526. import noticeIssuanceDetail from '@/views/bpm/documents/noticeIssuance/components/addOrEditDraw.vue';
  527. export default {
  528. name: 'HomePage',
  529. components: {
  530. vuedraggable,
  531. commonDialog,
  532. noticeIssuanceDetail
  533. },
  534. data() {
  535. return {
  536. currentDate: new Date(),
  537. calendarRange: [],
  538. commonFunctions: [],
  539. activeTab: 'notice',
  540. notifications: [],
  541. currentPage: 1,
  542. pageSize: 4,
  543. hasMore: true,
  544. portals: [],
  545. selectedCalendarType: 1,
  546. activeCalendarTooltipKey: '',
  547. calendarTooltipPopperOptions: {
  548. modifiers: {
  549. preventOverflow: {
  550. boundariesElement: 'viewport',
  551. padding: 12
  552. },
  553. flip: {
  554. boundariesElement: 'viewport',
  555. padding: 12
  556. }
  557. }
  558. },
  559. calendarTypeOptions: [
  560. { label: '标准生产日历', shortLabel: '标准生产', value: 1 },
  561. { label: '设备维护日历', shortLabel: '设备维护', value: 2 },
  562. { label: '人员排班日历', shortLabel: '人员排班', value: 3 }
  563. ],
  564. isDelFlag: false,
  565. commonDialogFlag: false,
  566. myProjectList: {},
  567. myWorkOrderList: {},
  568. myTaskList: {},
  569. myWorkCalendar: {},
  570. groupedWorkCalendar: {}, // 分组后的工作日历数据
  571. selectedScheduleDate: '', // 当前选中的日期
  572. dayDrawerVisible: false,
  573. currentDay: {},
  574. weekNames: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
  575. };
  576. },
  577. computed: {
  578. ...mapGetters(['user']),
  579. calendarViewTitle() {
  580. const date = new Date(this.currentDate);
  581. const year = date.getFullYear();
  582. const month = date.getMonth() + 1;
  583. return `${year} 年 ${month} 月`;
  584. },
  585. calendarCells() {
  586. if (!this.calendarRange || this.calendarRange.length < 2) {
  587. return [];
  588. }
  589. const start = this.parseCalendarDate(this.calendarRange[0]);
  590. const end = this.parseCalendarDate(this.calendarRange[1]);
  591. if (!start || !end) {
  592. return [];
  593. }
  594. const cells = [];
  595. const startOffset = (start.getDay() + 6) % 7;
  596. for (let i = 0; i < startOffset; i += 1) {
  597. cells.push({ key: `empty-${i}`, date: '' });
  598. }
  599. const cursor = new Date(start);
  600. while (cursor <= end) {
  601. const date = this.formatCalendarDate(cursor);
  602. cells.push({
  603. key: date,
  604. date,
  605. dayText: String(cursor.getDate()),
  606. isToday: this.isSameCalendarDate(cursor, new Date()),
  607. isSelected: date === this.selectedScheduleDate,
  608. isWeekend: [0, 6].includes(cursor.getDay()),
  609. schedules: this.groupedWorkCalendar[date] || []
  610. });
  611. cursor.setDate(cursor.getDate() + 1);
  612. }
  613. const trailingCount = cells.length % 7;
  614. if (trailingCount) {
  615. for (let i = trailingCount; i < 7; i += 1) {
  616. cells.push({ key: `empty-tail-${i}`, date: '' });
  617. }
  618. }
  619. return cells;
  620. },
  621. calendarSummary() {
  622. const days = this.calendarCells.filter((day) => day.date);
  623. const busyDays = days.filter((day) =>
  624. this.hasSchedule(day.date)
  625. ).length;
  626. const restDays = days.filter((day) => day.isWeekend).length;
  627. const total = days.reduce((sum, day) => {
  628. return sum + this.getScheduleCount(day.date);
  629. }, 0);
  630. return {
  631. total,
  632. busyDays,
  633. idleDays: Math.max(days.length - busyDays - restDays, 0)
  634. };
  635. },
  636. currentNotifications() {
  637. const notifications = this.notifications[this.activeTab] || [];
  638. return notifications;
  639. },
  640. dayDrawerTitle() {
  641. return this.currentDay.date
  642. ? `${this.currentDay.date} 日历详情`
  643. : '日历详情';
  644. },
  645. drawerDateMonth() {
  646. return this.currentDay.date ? this.currentDay.date.slice(0, 7) : '';
  647. },
  648. drawerDateDay() {
  649. return this.currentDay.date ? this.currentDay.date.slice(8, 10) : '';
  650. },
  651. relatedSectionTitle() {
  652. return this.isTeamQueueDrawer ? '排班详情' : '关联计划';
  653. },
  654. isEquipmentDrawer() {
  655. return (
  656. Number(this.selectedCalendarType) === 2 ||
  657. (this.currentDay.details || []).some(
  658. (item) => Number(item.calendarType) === 2
  659. )
  660. );
  661. },
  662. isTeamQueueDrawer() {
  663. return (
  664. Number(this.selectedCalendarType) === 3 ||
  665. (this.currentDay.details || []).some(
  666. (item) => Number(item.calendarType) === 3
  667. )
  668. );
  669. }
  670. },
  671. watch: {
  672. currentDate: {
  673. handler(newVal, oldVal) {
  674. // 检查月份是否变化
  675. if (
  676. newVal.getMonth() !== oldVal.getMonth() ||
  677. newVal.getFullYear() !== oldVal.getFullYear()
  678. ) {
  679. // 重新计算日期范围
  680. this.calendarRange = this.getMonthRange();
  681. // 重新获取日程数据
  682. this.getMyWorkCalendar();
  683. }
  684. // 日期变化时,处理选中日期的日程
  685. if (newVal !== oldVal) {
  686. const year = newVal.getFullYear();
  687. const month = String(newVal.getMonth() + 1).padStart(2, '0');
  688. const day = String(newVal.getDate()).padStart(2, '0');
  689. const dateStr = `${year}-${month}-${day}`;
  690. this.handleDateClick(dateStr);
  691. }
  692. },
  693. deep: true
  694. }
  695. },
  696. created() {
  697. this.calendarRange = this.getMonthRange();
  698. this.selectedScheduleDate = this.formatCalendarDate(new Date());
  699. this.getPortalsList();
  700. this.getCommonFunctionsList();
  701. this.getMyProjectTotal();
  702. this.getMyWorkOrderTotal();
  703. this.getMyTaskTotal();
  704. this.getMessageList();
  705. this.getMyWorkCalendar();
  706. },
  707. methods: {
  708. openUrl(item) {
  709. // 统一门户配置
  710. if (item.architType === '1') {
  711. // bs门户配置
  712. window.open(item.linkUrl);
  713. } else {
  714. let url = item.linkUrl.split('//');
  715. window.open(url[1], '_blank');
  716. }
  717. },
  718. handleNotificationsClick(item) {
  719. console.log('item~~~', item);
  720. if (this.activeTab === 'system') return;
  721. this.$refs.noticeIssuanceDetailRef.openDrawer({
  722. id: item.id,
  723. type: 'view'
  724. });
  725. // this.$router.push({
  726. // // query
  727. // query: {
  728. // id: item.id,
  729. // type: 'detail'
  730. // }
  731. // });
  732. },
  733. async getMyWorkCalendar() {
  734. const date = new Date(this.currentDate);
  735. const res = await viewCalendar({
  736. calendarType: this.selectedCalendarType,
  737. year: date.getFullYear(),
  738. month: date.getMonth() + 1,
  739. viewType: 'month'
  740. });
  741. const list = Array.isArray(res) ? res : [];
  742. const groupedData = {};
  743. list.forEach((item) => {
  744. const detail = this.normalizeWorkCalendarItem(item);
  745. if (!detail.date) {
  746. return;
  747. }
  748. if (!groupedData[detail.date]) {
  749. groupedData[detail.date] = [];
  750. }
  751. groupedData[detail.date].push(detail);
  752. });
  753. this.groupedWorkCalendar = groupedData;
  754. this.$nextTick(() => {
  755. this.scrollCurrentWeekToTop();
  756. });
  757. if (
  758. !this.selectedScheduleDate ||
  759. !this.isDateInCurrentMonth(this.selectedScheduleDate)
  760. ) {
  761. this.selectedScheduleDate = this.formatCalendarDate(new Date());
  762. }
  763. },
  764. hasSchedule(day) {
  765. return this.getScheduleCount(day) > 0;
  766. },
  767. handleCalendarTypeChange() {
  768. this.calendarRange = this.getMonthRange();
  769. this.groupedWorkCalendar = {};
  770. this.getMyWorkCalendar();
  771. },
  772. scrollCurrentWeekToTop() {
  773. const grid = this.$refs.calendarGrid;
  774. if (!grid) {
  775. return;
  776. }
  777. const today = this.formatCalendarDate(new Date());
  778. const todayIndex = this.calendarCells.findIndex(
  779. (day) => day.date === today
  780. );
  781. if (todayIndex === -1) {
  782. grid.scrollTop = 0;
  783. return;
  784. }
  785. const weekIndex = Math.floor(todayIndex / 7);
  786. const weekStartCell = grid.querySelectorAll('.calendar-cell')[
  787. weekIndex * 7
  788. ];
  789. if (!weekStartCell) {
  790. grid.scrollTop = 0;
  791. return;
  792. }
  793. const targetTop =
  794. weekStartCell.getBoundingClientRect().top -
  795. grid.getBoundingClientRect().top +
  796. grid.scrollTop;
  797. grid.scrollTo({
  798. top: Math.max(targetTop, 0),
  799. behavior: 'auto'
  800. });
  801. },
  802. handleDateClick(day) {
  803. console.log('day~~~', day);
  804. this.selectedScheduleDate = day;
  805. },
  806. showCalendarTooltip(day) {
  807. if (day.date) {
  808. this.activeCalendarTooltipKey = day.key;
  809. }
  810. },
  811. hideCalendarTooltip(day) {
  812. if (!day || this.activeCalendarTooltipKey === day.key) {
  813. this.activeCalendarTooltipKey = '';
  814. }
  815. },
  816. openCalendarDayDrawer(day) {
  817. if (!day || !day.date) {
  818. return;
  819. }
  820. this.hideCalendarTooltip(day);
  821. this.handleDateClick(day.date);
  822. this.currentDay = {
  823. ...day,
  824. details: this.getDayDetails(day),
  825. planCount: this.getDayPlanCount(day),
  826. scheduleStatus: this.hasSchedule(day.date),
  827. isRest: this.isRestDay(day),
  828. isConflict: this.isConflictDay(day),
  829. isTempAdjust: this.isTempAdjustDay(day),
  830. isMaintenance: this.isMaintenanceDay(day),
  831. relatedPlans: this.getDayRelatedPlans(day),
  832. conflictText: this.getDayConflictText(day)
  833. };
  834. this.dayDrawerVisible = true;
  835. },
  836. getScheduleListByDate() {
  837. if (
  838. this.selectedScheduleDate &&
  839. this.groupedWorkCalendar[this.selectedScheduleDate]
  840. ) {
  841. return this.groupedWorkCalendar[this.selectedScheduleDate];
  842. }
  843. return [];
  844. },
  845. parseCalendarDate(value) {
  846. if (!value) {
  847. return null;
  848. }
  849. const date = new Date(String(value).replace(/-/g, '/'));
  850. return Number.isNaN(date.getTime()) ? null : date;
  851. },
  852. formatCalendarDate(date) {
  853. const year = date.getFullYear();
  854. const month = String(date.getMonth() + 1).padStart(2, '0');
  855. const day = String(date.getDate()).padStart(2, '0');
  856. return `${year}-${month}-${day}`;
  857. },
  858. isSameCalendarDate(date, targetDate) {
  859. return (
  860. this.formatCalendarDate(date) === this.formatCalendarDate(targetDate)
  861. );
  862. },
  863. getScheduleCount(day) {
  864. const list =
  865. this.groupedWorkCalendar && this.groupedWorkCalendar[day]
  866. ? this.groupedWorkCalendar[day]
  867. : [];
  868. return list.filter((item) => this.isCalendarDetailScheduled(item))
  869. .length;
  870. },
  871. getCalendarDayClass(day) {
  872. if (!day.date) {
  873. return ['empty'];
  874. }
  875. return {
  876. scheduled: this.hasSchedule(day.date),
  877. rest: !this.hasSchedule(day.date) && this.isRestDay(day),
  878. idle: !this.hasSchedule(day.date) && !this.isRestDay(day),
  879. holiday: this.isHolidayDay(day),
  880. maintenance: this.isMaintenanceDay(day),
  881. temp: this.isTempAdjustDay(day),
  882. conflict: this.isConflictDay(day),
  883. 'is-today': day.isToday,
  884. 'is-selected': day.isSelected
  885. };
  886. },
  887. getCalendarDayTitle(day) {
  888. if (!day.date) {
  889. return '';
  890. }
  891. const details = this.getDayDetails(day);
  892. const rangeText = this.getDayPeriodList(day).join(',') || '无时段';
  893. const statusText = this.hasSchedule(day.date)
  894. ? '已排产'
  895. : '未排产/空闲';
  896. const conflictText = this.getDayConflictText(day);
  897. return [
  898. `日期:${day.date}`,
  899. `日期类型:${this.getDateTypeText(this.getDayDateType(day))}`,
  900. `时段:${rangeText}`,
  901. `排班状态:${statusText}`,
  902. `临时调整:${this.isTempAdjustDay(day) ? '是' : '否'}`,
  903. `冲突说明:${conflictText || '无'}`,
  904. `关联计划数:${this.getDayPlanCount(day)}`,
  905. `明细数:${details.length}`
  906. ].join('\n');
  907. },
  908. getCalendarDayStatus(day) {
  909. const count = this.getScheduleCount(day.date);
  910. if (count) {
  911. return '项';
  912. }
  913. return day.isWeekend ? '休息' : '待排';
  914. },
  915. getCalendarLoadWidth(day) {
  916. const count = this.getScheduleCount(day.date);
  917. if (!count) {
  918. return '0%';
  919. }
  920. return `${Math.min(100, Math.max(18, count * 5))}%`;
  921. },
  922. getCalendarDayType(day) {
  923. if (this.hasSchedule(day.date)) {
  924. return '生产';
  925. }
  926. return day.isWeekend ? '休息' : '空闲';
  927. },
  928. getDayDetails(day) {
  929. return day && Array.isArray(day.schedules) ? day.schedules : [];
  930. },
  931. getDayPlanCount(day) {
  932. const details = this.getDayDetails(day);
  933. const relatedPlans = this.getDayRelatedPlans(day);
  934. if (relatedPlans.length) {
  935. return relatedPlans.length;
  936. }
  937. return details.reduce((sum, item) => {
  938. return (
  939. sum + Number(item.relationPlanCount || item.planCount || 0)
  940. );
  941. }, 0);
  942. },
  943. getDetailRelatedPlans(item) {
  944. return [
  945. ...(Array.isArray(item.relationPlanList) ? item.relationPlanList : []),
  946. ...(Array.isArray(item.relationEamPlanList)
  947. ? item.relationEamPlanList
  948. : []),
  949. ...(Array.isArray(item.relationTeamQueueList)
  950. ? item.relationTeamQueueList
  951. : [])
  952. ];
  953. },
  954. getDayRelatedPlans(day) {
  955. const planMap = {};
  956. this.getDayDetails(day).forEach((item) => {
  957. this.getDetailRelatedPlans(item).forEach((plan, index) => {
  958. const key = this.getPlanUniqueKey(
  959. plan,
  960. `${item.id || item.date}-${index}`
  961. );
  962. planMap[key] = plan;
  963. });
  964. });
  965. return Object.values(planMap);
  966. },
  967. getCurrentDayRelatedPlans() {
  968. return Array.isArray(this.currentDay.relatedPlans)
  969. ? this.currentDay.relatedPlans
  970. : [];
  971. },
  972. getSelectedDayPlanCount() {
  973. return this.getDayPlanCount({
  974. schedules: this.getScheduleListByDate()
  975. });
  976. },
  977. isSelectedDayConflict() {
  978. return this.getScheduleListByDate().some(
  979. (item) => Number(item.isConflict) === 1
  980. );
  981. },
  982. getDayDateType(day) {
  983. const first = this.getDayDetails(day)[0];
  984. if (first) {
  985. return first.dateType;
  986. }
  987. return day.isWeekend ? 2 : 1;
  988. },
  989. getDayConflictText(day) {
  990. const conflict = this.getDayDetails(day).find(
  991. (item) => Number(item.isConflict) === 1
  992. );
  993. return conflict
  994. ? conflict.conflictText ||
  995. conflict.message ||
  996. conflict.remark ||
  997. '存在冲突'
  998. : '';
  999. },
  1000. getDayPeriodList(day) {
  1001. return this.getDayDetails(day)
  1002. .filter(
  1003. (item) =>
  1004. item.startHour || item.endHour || Number(item.dateType) !== 1
  1005. )
  1006. .slice(0, 2)
  1007. .map((item) => {
  1008. if (item.startHour || item.endHour) {
  1009. return `${item.startHour || ''}-${item.endHour || ''}`;
  1010. }
  1011. return item.typeText;
  1012. });
  1013. },
  1014. getDayRangeText(details = []) {
  1015. const ranges = details
  1016. .map((item) => this.getDetailTimeText(item))
  1017. .filter(Boolean);
  1018. return ranges.length ? ranges.join(',') : '无时段';
  1019. },
  1020. getDayStatusText(day) {
  1021. const count = this.getDayPlanCount(day);
  1022. if (count > 0) {
  1023. return `计划数:${count > 999 ? '999+' : count}`;
  1024. }
  1025. return '';
  1026. },
  1027. getDayPlanCountText(day) {
  1028. const count = this.getDayPlanCount(day);
  1029. return count > 999 ? '999+' : count;
  1030. },
  1031. isRestDay(day) {
  1032. const details = this.getDayDetails(day);
  1033. if (!details.length) {
  1034. return day.isWeekend;
  1035. }
  1036. return details.every((item) => [2, 3].includes(Number(item.dateType)));
  1037. },
  1038. isHolidayDay(day) {
  1039. return this.getDayDetails(day).some(
  1040. (item) => Number(item.dateType) === 3
  1041. );
  1042. },
  1043. isMaintenanceDay(day) {
  1044. return this.getDayDetails(day).some(
  1045. (item) => Number(item.dateType) === 4
  1046. );
  1047. },
  1048. isTempAdjustDay(day) {
  1049. return this.getDayDetails(day).some(
  1050. (item) =>
  1051. Number(item.isTempAdjust) === 1 || Number(item.dateType) === 5
  1052. );
  1053. },
  1054. isConflictDay(day) {
  1055. return this.getDayDetails(day).some(
  1056. (item) => Number(item.isConflict) === 1
  1057. );
  1058. },
  1059. getDetailTimeText(item) {
  1060. if (item.startHour || item.endHour) {
  1061. return `${item.startHour || ''}-${item.endHour || ''}`;
  1062. }
  1063. return '全天';
  1064. },
  1065. getDetailTitle(item) {
  1066. return item.dutyName || item.dateTypeName || item.typeText || this.getDateTypeText(item.dateType);
  1067. },
  1068. getPlanUniqueKey(plan = {}, fallback = '') {
  1069. return (
  1070. plan.planNo ||
  1071. plan.planCode ||
  1072. plan.productionPlanCode ||
  1073. plan.code ||
  1074. plan.id ||
  1075. plan.planId ||
  1076. fallback
  1077. );
  1078. },
  1079. getPlanCode(plan, index) {
  1080. return (
  1081. plan.planNo ||
  1082. plan.planCode ||
  1083. plan.code ||
  1084. plan.productionPlanCode ||
  1085. plan.id ||
  1086. plan.name ||
  1087. plan.planName ||
  1088. `计划${index + 1}`
  1089. );
  1090. },
  1091. getRelatedPlanCode(plan = {}, index = 0) {
  1092. return (
  1093. (this.isEquipmentDrawer
  1094. ? plan.deviceCode ||
  1095. plan.equipmentCode ||
  1096. plan.eamCode ||
  1097. plan.assetCode ||
  1098. plan.planNo ||
  1099. plan.planCode ||
  1100. plan.code
  1101. : this.getPlanCode(plan, index)) || '-'
  1102. );
  1103. },
  1104. getRelatedPlanName(plan = {}) {
  1105. return (
  1106. (this.isEquipmentDrawer
  1107. ? plan.deviceName ||
  1108. plan.equipmentName ||
  1109. plan.eamName ||
  1110. plan.assetName ||
  1111. this.getPlanNameFallback(plan)
  1112. : plan.taskName ||
  1113. plan.planName ||
  1114. plan.name ||
  1115. this.getPlanObjectName(plan)) || '-'
  1116. );
  1117. },
  1118. getRelatedQueueName(plan = {}) {
  1119. return plan.teamQueueName || plan.queueName || this.getPlanNameFallback(plan) || '-';
  1120. },
  1121. getRelatedQueueUserName(plan = {}) {
  1122. return plan.userName || plan.userNames || plan.staffName || '';
  1123. },
  1124. getPlanNameFallback(plan = {}) {
  1125. return (
  1126. plan.taskName ||
  1127. plan.planName ||
  1128. plan.name ||
  1129. plan.title ||
  1130. plan.productName ||
  1131. plan.bomCategoryName ||
  1132. plan.deviceName ||
  1133. plan.equipmentName ||
  1134. plan.eamName ||
  1135. plan.assetName ||
  1136. plan.machineName ||
  1137. plan.resourceName ||
  1138. ''
  1139. );
  1140. },
  1141. getPlanObjectName(plan) {
  1142. const calendarType = Number(plan.calendarType || this.selectedCalendarType);
  1143. if (calendarType === 2) {
  1144. return (
  1145. plan.equipmentName ||
  1146. plan.deviceName ||
  1147. plan.eamName ||
  1148. plan.assetName ||
  1149. plan.machineName ||
  1150. plan.resourceName ||
  1151. this.getPlanNameFallback(plan) ||
  1152. ''
  1153. );
  1154. }
  1155. if (calendarType === 3) {
  1156. return (
  1157. plan.teamQueueName ||
  1158. plan.queueName ||
  1159. plan.userName ||
  1160. this.getPlanNameFallback(plan) ||
  1161. ''
  1162. );
  1163. }
  1164. return (
  1165. this.getPlanNameFallback(plan) ||
  1166. plan.equipmentName ||
  1167. plan.deviceName ||
  1168. plan.machineName ||
  1169. plan.resourceName ||
  1170. plan.productionLineName ||
  1171. plan.workCenterName ||
  1172. plan.productName ||
  1173. plan.materialName ||
  1174. ''
  1175. );
  1176. },
  1177. getPlanQuantity(plan) {
  1178. return (
  1179. plan.requiredFormingNum ||
  1180. plan.quantity ||
  1181. plan.qty ||
  1182. plan.planQty ||
  1183. plan.amount ||
  1184. ''
  1185. );
  1186. },
  1187. getPlanStartTime(plan) {
  1188. return (
  1189. plan.displayStartTime ||
  1190. plan.startTime ||
  1191. plan.planStartTime ||
  1192. plan.beginTime ||
  1193. ''
  1194. );
  1195. },
  1196. getPlanEndTime(plan) {
  1197. return (
  1198. plan.displayEndTime ||
  1199. plan.endTime ||
  1200. plan.planEndTime ||
  1201. plan.finishTime ||
  1202. ''
  1203. );
  1204. },
  1205. getPlanTimeRange(plan) {
  1206. const start = this.getPlanStartTime(plan);
  1207. const end = this.getPlanEndTime(plan);
  1208. if (start && end) {
  1209. return `${start} - ${end}`;
  1210. }
  1211. return start || end || '';
  1212. },
  1213. getDateTypeText(dateType) {
  1214. const map = {
  1215. 1: '正常工作日',
  1216. 2: '休息日',
  1217. 3: '节假日',
  1218. 4: '设备检修期',
  1219. 5: '临时调整时段'
  1220. };
  1221. return map[Number(dateType)] || '日历明细';
  1222. },
  1223. getDetailPlanCount(item) {
  1224. const relationPlanList = Array.isArray(item.relationPlanList)
  1225. ? item.relationPlanList
  1226. : [];
  1227. const relationEamPlanList = Array.isArray(item.relationEamPlanList)
  1228. ? item.relationEamPlanList
  1229. : [];
  1230. const relationTeamQueueList = Array.isArray(item.relationTeamQueueList)
  1231. ? item.relationTeamQueueList
  1232. : [];
  1233. const relationCountMap = {
  1234. 2: relationEamPlanList.length,
  1235. 3: relationTeamQueueList.length
  1236. };
  1237. const mappedCount =
  1238. relationCountMap[
  1239. Number(item.calendarType || this.selectedCalendarType)
  1240. ];
  1241. return mappedCount !== undefined
  1242. ? mappedCount
  1243. : Number(
  1244. item.relationPlanCount ||
  1245. item.planCount ||
  1246. relationPlanList.length ||
  1247. 0
  1248. );
  1249. },
  1250. isCalendarDetailScheduled(item) {
  1251. return (
  1252. Number(item.scheduleStatus) === 1 || this.getDetailPlanCount(item) > 0
  1253. );
  1254. },
  1255. normalizeCalendarTime(value) {
  1256. if (!value) {
  1257. return '';
  1258. }
  1259. const text = String(value);
  1260. const time = text.includes(' ') ? text.split(' ')[1] : text;
  1261. return time.slice(0, 5);
  1262. },
  1263. normalizeWorkCalendarItem(item) {
  1264. const startDate =
  1265. item.startTime && String(item.startTime).includes(' ')
  1266. ? String(item.startTime).split(' ')[0]
  1267. : '';
  1268. const date = item.calendarDate || item.date || startDate;
  1269. const dateType = Number(item.dateType || 1);
  1270. const relationPlanCount = this.getDetailPlanCount(item);
  1271. const typeText = item.dateTypeName || this.getDateTypeText(dateType);
  1272. return {
  1273. ...item,
  1274. date,
  1275. time: this.normalizeCalendarTime(item.startTime),
  1276. startHour: this.normalizeCalendarTime(item.startTime),
  1277. endHour: this.normalizeCalendarTime(item.endTime),
  1278. dateType,
  1279. typeText,
  1280. relationPlanCount,
  1281. scheduleStatus: Number(item.scheduleStatus || 0),
  1282. title:
  1283. item.title ||
  1284. item.calendarName ||
  1285. item.planName ||
  1286. item.remark ||
  1287. typeText,
  1288. name:
  1289. item.name ||
  1290. item.shiftName ||
  1291. item.teamName ||
  1292. item.workCenterName ||
  1293. ''
  1294. };
  1295. },
  1296. isDateInCurrentMonth(date) {
  1297. if (!date) {
  1298. return false;
  1299. }
  1300. const current = new Date(this.currentDate);
  1301. return (
  1302. String(date).slice(0, 7) ===
  1303. this.formatCalendarDate(current).slice(0, 7)
  1304. );
  1305. },
  1306. getCalendarDaySummary(day) {
  1307. const first = day.schedules && day.schedules[0];
  1308. if (!first) {
  1309. return '';
  1310. }
  1311. return (
  1312. first.title ||
  1313. first.name ||
  1314. `${first.startHour || ''}~${first.endHour || ''}`
  1315. );
  1316. },
  1317. handleToPro() {
  1318. this.handelRouterTo('/page-pro/project-initiation');
  1319. },
  1320. handleTo(key) {
  1321. switch (key) {
  1322. case '测试项目':
  1323. case '研发项目':
  1324. case '实施项目':
  1325. case '开发项目':
  1326. this.handelRouterTo('/page-pro/project-initiation');
  1327. break;
  1328. case '运维工单':
  1329. this.handelRouterTo(
  1330. '/page-eam/maintenance/patrol?isWt=true&title=工单'
  1331. );
  1332. break;
  1333. case '质检工单':
  1334. this.handelRouterTo('/page-qms/inspectionWork');
  1335. break;
  1336. case '生产工单':
  1337. this.handelRouterTo('/page-mes/produceOrder?isWt=true');
  1338. break;
  1339. case '盘点工单':
  1340. this.handelRouterTo(
  1341. '/page-wms/warehouseManagement/stocktaking/work'
  1342. );
  1343. break;
  1344. case '盘点任务':
  1345. this.handelRouterTo('');
  1346. break;
  1347. case '生产任务':
  1348. this.handelRouterTo('/page-mes/rw');
  1349. break;
  1350. case '质检任务':
  1351. this.handelRouterTo('/page-qms/inspectionProjectTask');
  1352. break;
  1353. case '项目任务':
  1354. this.handelRouterTo('/page-pro/task/myTaskList');
  1355. break;
  1356. }
  1357. },
  1358. async getMessageList() {
  1359. let list = [];
  1360. let total = 0;
  1361. if (this.activeTab === 'notice') {
  1362. const res = await noticeDocumentPageV1API({
  1363. pageNum: this.currentPage,
  1364. size: 5,
  1365. approvalStatus: 2
  1366. });
  1367. console.log('noticeDocumentPageV1API~~~', res);
  1368. total = res.count || 0;
  1369. list = (res.list || []).map((item) => {
  1370. // 提取 HTML 中的图片地址
  1371. const images = [];
  1372. const content = item.content || '';
  1373. const imgRegex = /<img[^>]+src=["']([^"']+)["']/gi;
  1374. let match;
  1375. while ((match = imgRegex.exec(content)) !== null) {
  1376. images.push(match[1]);
  1377. }
  1378. // 提取纯文本内容(去除 HTML 标签和空格)
  1379. const textContent = content
  1380. .replace(/<[^>]+>/g, '')
  1381. .replace(/&nbsp;/g, '')
  1382. .replace(/\s+/g, '')
  1383. .trim();
  1384. return {
  1385. id: item.id,
  1386. title: item.title,
  1387. content: textContent,
  1388. images: images,
  1389. createUser: '拟稿人:' + item.draftAuthor,
  1390. department: '发文机关:' + item.issuingAuthority,
  1391. createTime: item.createTime
  1392. };
  1393. });
  1394. } else if (this.activeTab === 'industry') {
  1395. list = [];
  1396. } else if (this.activeTab === 'system') {
  1397. const res = await notifyMessagePageAPI({
  1398. pageNum: this.currentPage,
  1399. size: 5
  1400. // userId: this.user.info.userId,
  1401. });
  1402. total = res.count || 0;
  1403. list = (res.list || []).map((item) => {
  1404. return {
  1405. title: item.templateName,
  1406. content: item.templateContent,
  1407. createUser: '创建人:' + item.userName,
  1408. department: item.department,
  1409. createTime: item.createTime
  1410. };
  1411. });
  1412. }
  1413. // 如果是第一页,直接赋值;否则拼接到后面
  1414. if (this.currentPage === 1) {
  1415. this.notifications = list;
  1416. } else {
  1417. this.notifications = [...this.notifications, ...list];
  1418. }
  1419. // 判断是否还有更多数据
  1420. this.hasMore = this.notifications.length < total;
  1421. console.log(
  1422. 'notifications~~~',
  1423. this.notifications,
  1424. 'hasMore:',
  1425. this.hasMore
  1426. );
  1427. },
  1428. async getMyProjectTotal() {
  1429. this.myProjectList = await getMyProjectTotal({});
  1430. },
  1431. async getMyWorkOrderTotal() {
  1432. this.myWorkOrderList = await getMyWorkOrderTotal({});
  1433. },
  1434. async getMyTaskTotal() {
  1435. this.myTaskList = await getMyTaskTotal({});
  1436. },
  1437. handleAdd() {
  1438. // if(this.commonFunctions.length > 12) return this.$message.warning('最多只能添加12个常用功能');
  1439. this.commonDialogFlag = true;
  1440. this.$nextTick(() => {
  1441. this.$refs.commonDialogRef.init();
  1442. });
  1443. },
  1444. handleEdit() {
  1445. this.isDelFlag = !this.isDelFlag;
  1446. },
  1447. handleDel(item, index) {
  1448. if (this.isDelFlag) return this.handleCommonListDel(item, index);
  1449. let urlPath = item.topUrl + item.url;
  1450. console.log('urlPath~~~', urlPath);
  1451. this.handelRouterTo(urlPath);
  1452. },
  1453. async handleCommonListDel(item, index) {
  1454. await userResourceDeleteAPI([item.id]);
  1455. this.commonFunctions.splice(index, 1);
  1456. },
  1457. handelRouterTo(path) {
  1458. window.history.pushState(null, '', path);
  1459. },
  1460. getIconPath(pic) {
  1461. if (pic) {
  1462. // 如果后端返回的是静态路径字符串,需要通过 require() 处理
  1463. return require(`@/assets/home/commonIcon/${pic}`);
  1464. }
  1465. // 如果没有 pic 字段,使用默认图标
  1466. return require('@/assets/home/commonIcon/icon_0.svg');
  1467. },
  1468. async getCommonFunctionsList() {
  1469. this.commonFunctions = await userResourceListAPI();
  1470. console.log('commonFunctions~~~', this.commonFunctions);
  1471. },
  1472. async getPortalsList() {
  1473. let { list } = await getList({ pageNum: 1, size: 9999 });
  1474. this.portals = list.map((item) => {
  1475. return {
  1476. ...item,
  1477. img:
  1478. window.location.origin +
  1479. '/api/main/file/getFile?objectName=' +
  1480. item.iconPath
  1481. };
  1482. });
  1483. console.log(this.portals);
  1484. },
  1485. handleTabClick() {
  1486. // 切换标签页时重置页码和hasMore
  1487. this.currentPage = 1;
  1488. this.hasMore = true;
  1489. this.notifications = [];
  1490. this.getMessageList();
  1491. },
  1492. loadMoreNotifications() {
  1493. // 加载更多通知(增加页码)
  1494. this.currentPage++;
  1495. this.getMessageList();
  1496. // switch(this.activeTab) {
  1497. // case 'notice':
  1498. // this.handelRouterTo('/page-wt/documents/noticeIssuance');
  1499. // break;
  1500. // case 'industry':
  1501. // this.handelRouterTo('/industry');
  1502. // break;
  1503. // case 'system':
  1504. // this.handelRouterTo('/page-wt/message');
  1505. // break;
  1506. // }
  1507. },
  1508. // 获取选中当月的范围
  1509. getMonthRange() {
  1510. const now = new Date(this.currentDate);
  1511. const year = now.getFullYear();
  1512. const month = now.getMonth();
  1513. // 当月第一天
  1514. const firstDay = new Date(year, month, 1);
  1515. // 下月第一天,减一天就是当月最后一天
  1516. const lastDay = new Date(year, month + 1, 0);
  1517. const formatDate = (date) => {
  1518. const year = date.getFullYear();
  1519. const month = String(date.getMonth() + 1).padStart(2, '0');
  1520. const day = String(date.getDate()).padStart(2, '0');
  1521. return `${year}-${month}-${day}`;
  1522. };
  1523. return [formatDate(firstDay), formatDate(lastDay)];
  1524. }
  1525. }
  1526. };
  1527. </script>
  1528. <style lang="scss" scoped>
  1529. .home-container {
  1530. width: 100%;
  1531. padding: 10px;
  1532. box-sizing: border-box;
  1533. display: flex;
  1534. flex-direction: column;
  1535. height: calc(100vh - 96px);
  1536. min-height: 0;
  1537. overflow: hidden;
  1538. .home-top-row {
  1539. flex: 0 0 148px;
  1540. height: 148px;
  1541. }
  1542. .home-overview-row {
  1543. flex: 0 0 232px;
  1544. height: 232px;
  1545. }
  1546. .home-bottom-row {
  1547. flex: 1 1 auto;
  1548. min-height: 0;
  1549. height: auto;
  1550. }
  1551. .card-content {
  1552. &.common-function-card {
  1553. overflow-x: auto;
  1554. overflow-y: hidden;
  1555. white-space: nowrap;
  1556. padding-bottom: 8px;
  1557. &::-webkit-scrollbar {
  1558. height: 6px;
  1559. }
  1560. &::-webkit-scrollbar-track {
  1561. background: #f1f1f1;
  1562. border-radius: 3px;
  1563. }
  1564. &::-webkit-scrollbar-thumb {
  1565. background: #c1c1c1;
  1566. border-radius: 3px;
  1567. }
  1568. &::-webkit-scrollbar-thumb:hover {
  1569. background: #a8a8a8;
  1570. }
  1571. .common-function {
  1572. flex-wrap: nowrap;
  1573. display: inline-flex;
  1574. }
  1575. }
  1576. &.portal-content {
  1577. max-height: 172px;
  1578. overflow-y: auto;
  1579. overflow-x: hidden;
  1580. padding-right: 8px;
  1581. flex-shrink: 0;
  1582. min-height: 0;
  1583. .portal {
  1584. max-height: 100%;
  1585. overflow-y: auto;
  1586. overflow-x: hidden;
  1587. }
  1588. &::-webkit-scrollbar {
  1589. width: 6px;
  1590. }
  1591. &::-webkit-scrollbar-track {
  1592. background: #f1f1f1;
  1593. border-radius: 3px;
  1594. }
  1595. &::-webkit-scrollbar-thumb {
  1596. background: #c1c1c1;
  1597. border-radius: 3px;
  1598. }
  1599. &::-webkit-scrollbar-thumb:hover {
  1600. background: #a8a8a8;
  1601. }
  1602. }
  1603. &.calendar-content {
  1604. height: 100%;
  1605. min-height: 0;
  1606. overflow-y: hidden;
  1607. overflow-x: hidden;
  1608. padding: 8px 8px 10px;
  1609. box-sizing: border-box;
  1610. &::-webkit-scrollbar {
  1611. width: 6px;
  1612. }
  1613. &::-webkit-scrollbar-track {
  1614. background: #f1f1f1;
  1615. border-radius: 3px;
  1616. }
  1617. &::-webkit-scrollbar-thumb {
  1618. background: #c1c1c1;
  1619. border-radius: 3px;
  1620. }
  1621. &::-webkit-scrollbar-thumb:hover {
  1622. background: #a8a8a8;
  1623. }
  1624. }
  1625. .common-function {
  1626. display: flex;
  1627. flex-wrap: wrap;
  1628. justify-content: flex-start;
  1629. align-items: center;
  1630. .common-function-item {
  1631. display: flex;
  1632. flex-direction: column;
  1633. justify-content: center;
  1634. align-items: center;
  1635. margin: 0 18px;
  1636. border-radius: 5px;
  1637. cursor: pointer;
  1638. transition: all 0.3s ease-in-out;
  1639. .common-function-item-icon {
  1640. width: 50px;
  1641. height: 50px;
  1642. box-shadow: 0px 1px 2px 1px #eae7e7;
  1643. background: #f8fafc;
  1644. border-radius: 17px;
  1645. display: flex;
  1646. justify-content: center;
  1647. align-items: center;
  1648. img {
  1649. width: 30px;
  1650. height: 30px;
  1651. }
  1652. }
  1653. .common-function-item-name {
  1654. font-size: 14px;
  1655. font-weight: 400;
  1656. color: #3f3e3e;
  1657. margin-top: 10px;
  1658. }
  1659. }
  1660. .div-del {
  1661. // background: #cccccc !important;
  1662. position: relative;
  1663. }
  1664. .div-del::after {
  1665. content: '—';
  1666. position: absolute;
  1667. padding: 5px; /* 按钮的内边距 */
  1668. background-color: rgba(255, 0, 0, 0.91);
  1669. color: white;
  1670. border-radius: 50%;
  1671. }
  1672. }
  1673. .overview-container {
  1674. .overview {
  1675. padding: 16px;
  1676. box-sizing: border-box;
  1677. border-radius: 17px;
  1678. box-shadow: -1px 1px 0px 1px #ececec, 1px -1px 0px 1px #ececec;
  1679. border: 1px solid #ececec;
  1680. .overview-top {
  1681. display: flex;
  1682. justify-content: flex-start;
  1683. align-items: center;
  1684. .overview-top-img {
  1685. width: 66px;
  1686. height: 66px;
  1687. border-radius: 50%;
  1688. margin-right: 10px;
  1689. img {
  1690. width: 100%;
  1691. height: 100%;
  1692. }
  1693. }
  1694. .overview-top-content {
  1695. display: flex;
  1696. flex-direction: column;
  1697. justify-content: center;
  1698. align-items: flex-start;
  1699. .overview-top-content-name {
  1700. font-size: 18px;
  1701. font-weight: 500;
  1702. color: #525252;
  1703. }
  1704. .overview-top-content-count {
  1705. font-size: 28px;
  1706. font-weight: 500;
  1707. color: #2d80ee;
  1708. line-height: 42px;
  1709. // margin-top: 8px;
  1710. }
  1711. }
  1712. }
  1713. .el-divider {
  1714. margin: 12px 0;
  1715. }
  1716. .overview-bottom {
  1717. display: grid;
  1718. grid-template-columns: repeat(2, 1fr);
  1719. grid-gap: 10px;
  1720. max-height: 45px;
  1721. overflow-y: auto;
  1722. .overview-bottom-item {
  1723. display: flex;
  1724. justify-content: flex-start;
  1725. align-items: center;
  1726. cursor: pointer;
  1727. .overview-bottom-item-name {
  1728. font-size: 14px;
  1729. font-weight: 400;
  1730. color: #686767;
  1731. margin-right: 30px;
  1732. }
  1733. .overview-bottom-item-count {
  1734. font-size: 15px;
  1735. font-weight: 400;
  1736. color: #303133;
  1737. }
  1738. }
  1739. }
  1740. }
  1741. }
  1742. .portal {
  1743. .portal-item {
  1744. margin-bottom: 12px;
  1745. .portal-item-content {
  1746. display: flex;
  1747. justify-content: flex-start;
  1748. align-items: center;
  1749. border-radius: 11px;
  1750. border: 1px solid #ebebeb;
  1751. box-shadow: 0px 4px 4px #a7a7a740;
  1752. padding: 6px 4px;
  1753. cursor: pointer;
  1754. .portal-item-img {
  1755. width: 40px;
  1756. height: 32px;
  1757. border-radius: 5px;
  1758. margin-right: 10px;
  1759. img {
  1760. width: 100%;
  1761. height: 100%;
  1762. }
  1763. }
  1764. .portal-item-name {
  1765. font-size: 13px;
  1766. font-weight: bold;
  1767. color: #303133;
  1768. }
  1769. }
  1770. }
  1771. }
  1772. .work-calendar {
  1773. height: 100%;
  1774. min-height: 0;
  1775. border: 1px solid #e4edf7;
  1776. border-radius: 6px;
  1777. background: #fff;
  1778. box-shadow: 0 10px 24px rgba(31, 45, 61, 0.05);
  1779. overflow: hidden;
  1780. display: flex;
  1781. flex-direction: column;
  1782. margin: 4px 0 0;
  1783. .calendar-view-title {
  1784. display: flex;
  1785. align-items: center;
  1786. justify-content: space-between;
  1787. gap: 8px;
  1788. height: 38px;
  1789. flex: 0 0 38px;
  1790. padding: 0 12px;
  1791. border-bottom: 1px solid #e4edf7;
  1792. color: #303133;
  1793. font-size: 15px;
  1794. font-weight: 600;
  1795. background: linear-gradient(90deg, #f4f9ff, #ffffff);
  1796. .calendar-title-tools {
  1797. display: flex;
  1798. align-items: center;
  1799. justify-content: flex-end;
  1800. min-width: 0;
  1801. .calendar-type-switch {
  1802. display: inline-flex;
  1803. padding: 3px;
  1804. border: 1px solid #d9e5f2;
  1805. border-radius: 8px;
  1806. background: #f6f9fd;
  1807. white-space: nowrap;
  1808. ::v-deep .el-radio-button__inner {
  1809. height: 28px;
  1810. padding: 0 13px;
  1811. border: 0;
  1812. border-radius: 6px;
  1813. color: #536273;
  1814. font-size: 12px;
  1815. font-weight: 700;
  1816. line-height: 28px;
  1817. background: transparent;
  1818. box-shadow: none;
  1819. }
  1820. ::v-deep .el-radio-button:first-child .el-radio-button__inner {
  1821. border-radius: 6px;
  1822. }
  1823. ::v-deep .el-radio-button:last-child .el-radio-button__inner {
  1824. border-radius: 6px;
  1825. }
  1826. ::v-deep
  1827. .el-radio-button__orig-radio:checked
  1828. + .el-radio-button__inner {
  1829. background: #2f80ed;
  1830. color: #fff;
  1831. box-shadow: 0 4px 10px rgba(47, 128, 237, 0.24);
  1832. }
  1833. }
  1834. }
  1835. }
  1836. .calendar-week {
  1837. display: grid;
  1838. grid-template-columns: repeat(7, minmax(0, 1fr));
  1839. border-top: 2px solid #2f80ed;
  1840. border-bottom: 1px solid #d8e6f5;
  1841. flex: 0 0 30px;
  1842. background: linear-gradient(180deg, #f1f7ff 0%, #ffffff 100%);
  1843. span {
  1844. position: relative;
  1845. height: 30px;
  1846. line-height: 30px;
  1847. text-align: center;
  1848. color: #344968;
  1849. font-size: 12px;
  1850. font-weight: 700;
  1851. background: transparent;
  1852. &::after {
  1853. content: '';
  1854. position: absolute;
  1855. left: 50%;
  1856. bottom: 4px;
  1857. width: 16px;
  1858. height: 2px;
  1859. border-radius: 2px;
  1860. background: #2f80ed;
  1861. transform: translateX(-50%);
  1862. opacity: 0.72;
  1863. }
  1864. &:nth-child(6),
  1865. &:nth-child(7) {
  1866. color: #c45656;
  1867. &::after {
  1868. background: #f56c6c;
  1869. }
  1870. }
  1871. }
  1872. }
  1873. .calendar-grid {
  1874. display: grid;
  1875. grid-template-columns: repeat(7, minmax(0, 1fr));
  1876. grid-auto-rows: minmax(96px, auto);
  1877. flex: 1 1 auto;
  1878. min-height: 0;
  1879. overflow-y: auto;
  1880. overflow-x: hidden;
  1881. background: #e5edf6;
  1882. gap: 1px;
  1883. padding-bottom: 8px;
  1884. &::-webkit-scrollbar {
  1885. width: 6px;
  1886. }
  1887. &::-webkit-scrollbar-track {
  1888. background: #f1f4f8;
  1889. border-radius: 3px;
  1890. }
  1891. &::-webkit-scrollbar-thumb {
  1892. background: #aeb8c4;
  1893. border-radius: 3px;
  1894. }
  1895. &::-webkit-scrollbar-thumb:hover {
  1896. background: #8c96a3;
  1897. }
  1898. }
  1899. .calendar-cell {
  1900. min-height: 96px;
  1901. padding: 6px 7px 6px 10px;
  1902. border: 0;
  1903. background: #fff;
  1904. position: relative;
  1905. cursor: pointer;
  1906. overflow: hidden;
  1907. transition: box-shadow 0.2s, background 0.2s, transform 0.2s;
  1908. &::before {
  1909. content: '';
  1910. position: absolute;
  1911. left: 0;
  1912. top: 0;
  1913. width: 3px;
  1914. height: 100%;
  1915. background: transparent;
  1916. }
  1917. &:hover {
  1918. z-index: 1;
  1919. box-shadow: inset 0 0 0 1px #409eff,
  1920. 0 8px 18px rgba(64, 158, 255, 0.12);
  1921. }
  1922. &.empty {
  1923. cursor: default;
  1924. background: #f8fafc;
  1925. }
  1926. &.scheduled {
  1927. background: #ecf5ff;
  1928. &::before {
  1929. background: #409eff;
  1930. }
  1931. }
  1932. &.rest {
  1933. background: #f2f3f5;
  1934. &::before {
  1935. background: #a8abb2;
  1936. }
  1937. }
  1938. &.idle {
  1939. background: #fff;
  1940. }
  1941. &.holiday {
  1942. background: #fff7e8;
  1943. &::before {
  1944. background: #f59e0b;
  1945. }
  1946. }
  1947. &.maintenance {
  1948. background: #fff3e6;
  1949. &::before {
  1950. background: #e6a23c;
  1951. }
  1952. }
  1953. &.temp {
  1954. background: #f5efff;
  1955. &::before {
  1956. background: #9254de;
  1957. }
  1958. }
  1959. &.conflict {
  1960. background: #fef0f0;
  1961. animation: conflictFlash 1.4s infinite;
  1962. &::before {
  1963. background: #f56c6c;
  1964. }
  1965. }
  1966. &.is-today {
  1967. box-shadow: inset 0 0 0 1px rgba(64, 158, 255, 0.45);
  1968. }
  1969. &.is-selected {
  1970. box-shadow: inset 0 0 0 2px #409eff;
  1971. }
  1972. }
  1973. .day-head,
  1974. .day-foot {
  1975. display: flex;
  1976. align-items: center;
  1977. justify-content: space-between;
  1978. min-height: 18px;
  1979. }
  1980. .day-head {
  1981. gap: 3px;
  1982. span {
  1983. display: inline-flex;
  1984. align-items: center;
  1985. justify-content: center;
  1986. min-width: auto;
  1987. height: 18px;
  1988. padding: 0;
  1989. border-radius: 0;
  1990. color: #303133;
  1991. font-size: 15px;
  1992. font-weight: 700;
  1993. }
  1994. em {
  1995. display: inline-flex;
  1996. align-items: center;
  1997. justify-content: center;
  1998. width: 16px;
  1999. height: 16px;
  2000. margin-left: auto;
  2001. border-radius: 50%;
  2002. background: #409eff;
  2003. color: #fff;
  2004. font-size: 11px;
  2005. font-style: normal;
  2006. line-height: 1;
  2007. text-align: center;
  2008. }
  2009. }
  2010. .temp-adjust-tag {
  2011. height: 15px;
  2012. padding: 0 3px;
  2013. border-color: #d3b7ff;
  2014. background: #f4efff;
  2015. color: #7c3fd1;
  2016. font-size: 9px;
  2017. font-weight: 700;
  2018. line-height: 13px;
  2019. white-space: nowrap;
  2020. transform: scale(0.88);
  2021. transform-origin: right top;
  2022. margin-left: auto;
  2023. }
  2024. .holiday-tag {
  2025. height: 15px;
  2026. padding: 0 3px;
  2027. border-color: #f7c873;
  2028. background: #fff3d6;
  2029. color: #b76e00;
  2030. font-size: 9px;
  2031. font-weight: 700;
  2032. line-height: 13px;
  2033. white-space: nowrap;
  2034. transform: scale(0.86);
  2035. transform-origin: right top;
  2036. }
  2037. .day-body {
  2038. min-height: 34px;
  2039. margin: 4px 0;
  2040. }
  2041. .period-pill {
  2042. display: block;
  2043. width: fit-content;
  2044. max-width: 100%;
  2045. margin-bottom: 3px;
  2046. padding: 0 4px;
  2047. border: 1px solid rgba(144, 147, 153, 0.16);
  2048. border-radius: 4px;
  2049. background: rgba(255, 255, 255, 0.72);
  2050. color: #606266;
  2051. font-size: 10px;
  2052. line-height: 16px;
  2053. white-space: nowrap;
  2054. overflow: hidden;
  2055. text-overflow: ellipsis;
  2056. }
  2057. .day-foot {
  2058. position: relative;
  2059. display: flex;
  2060. align-items: center;
  2061. justify-content: flex-start;
  2062. color: #5f6f85;
  2063. gap: 2px;
  2064. font-size: 11px;
  2065. line-height: 16px;
  2066. white-space: nowrap;
  2067. min-width: 0;
  2068. min-height: 18px;
  2069. padding-right: 0;
  2070. span {
  2071. overflow: hidden;
  2072. text-overflow: ellipsis;
  2073. min-width: 0;
  2074. }
  2075. .plan-text {
  2076. color: #5f6f85;
  2077. flex: 0 0 auto;
  2078. max-width: none;
  2079. overflow: visible;
  2080. text-overflow: clip;
  2081. font-size: 10px;
  2082. font-weight: 600;
  2083. letter-spacing: -0.2px;
  2084. line-height: 16px;
  2085. }
  2086. .conflict-text {
  2087. flex: 0 0 auto;
  2088. margin-left: auto;
  2089. color: #ff4d4f;
  2090. font-size: 11px;
  2091. font-weight: 700;
  2092. }
  2093. }
  2094. }
  2095. }
  2096. }
  2097. .mt-20 {
  2098. margin-top: 6px;
  2099. }
  2100. .box-col {
  2101. height: 100%;
  2102. }
  2103. .box-card {
  2104. height: 100%;
  2105. margin-bottom: 0;
  2106. display: flex;
  2107. flex-direction: column;
  2108. ::v-deep .el-card__header {
  2109. flex: 0 0 auto;
  2110. padding: 10px 8px;
  2111. font-weight: bold;
  2112. color: #303133;
  2113. }
  2114. ::v-deep .el-card__body {
  2115. flex: 1 1 auto;
  2116. min-height: 0;
  2117. padding: 12px;
  2118. }
  2119. }
  2120. .calendar-card {
  2121. ::v-deep .el-card__body {
  2122. display: flex;
  2123. flex-direction: column;
  2124. padding: 8px 10px;
  2125. }
  2126. }
  2127. ::v-deep .calendar-day-tooltip {
  2128. max-width: 310px;
  2129. padding: 0;
  2130. border: 1px solid #cfe0f5;
  2131. border-radius: 8px;
  2132. box-shadow: 0 12px 28px rgba(31, 45, 61, 0.16);
  2133. .popper__arrow {
  2134. border-top-color: #cfe0f5 !important;
  2135. border-bottom-color: #cfe0f5 !important;
  2136. }
  2137. }
  2138. .calendar-tooltip-content {
  2139. min-width: 260px;
  2140. padding: 12px 14px;
  2141. color: #303133;
  2142. background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);
  2143. border-radius: 8px;
  2144. }
  2145. .tooltip-head {
  2146. display: flex;
  2147. align-items: center;
  2148. justify-content: space-between;
  2149. gap: 12px;
  2150. margin-bottom: 10px;
  2151. padding-bottom: 8px;
  2152. border-bottom: 1px solid #e6eef8;
  2153. strong {
  2154. color: #1f3d66;
  2155. font-size: 15px;
  2156. line-height: 20px;
  2157. font-weight: 700;
  2158. }
  2159. span {
  2160. height: 20px;
  2161. padding: 0 8px;
  2162. border-radius: 10px;
  2163. background: #fff1f0;
  2164. color: #f5222d;
  2165. font-size: 12px;
  2166. font-weight: 700;
  2167. line-height: 20px;
  2168. }
  2169. }
  2170. .tooltip-row {
  2171. display: grid;
  2172. grid-template-columns: 68px minmax(0, 1fr);
  2173. gap: 10px;
  2174. align-items: start;
  2175. margin-top: 7px;
  2176. font-size: 13px;
  2177. line-height: 20px;
  2178. span {
  2179. color: #7b8794;
  2180. }
  2181. strong {
  2182. color: #2f3a4a;
  2183. font-weight: 600;
  2184. word-break: break-all;
  2185. }
  2186. }
  2187. .tooltip-stats {
  2188. display: flex;
  2189. gap: 8px;
  2190. margin-top: 12px;
  2191. span {
  2192. padding: 3px 9px;
  2193. border-radius: 12px;
  2194. background: #edf5ff;
  2195. color: #1f66c2;
  2196. font-size: 12px;
  2197. font-weight: 700;
  2198. line-height: 18px;
  2199. }
  2200. }
  2201. ::v-deep .calendar-day-drawer {
  2202. display: flex;
  2203. flex-direction: column;
  2204. .el-drawer__header {
  2205. flex: 0 0 auto;
  2206. margin-bottom: 0;
  2207. padding: 22px 24px 14px;
  2208. border-bottom: 1px solid #e8eef7;
  2209. color: #303133;
  2210. font-size: 18px;
  2211. font-weight: 600;
  2212. background: linear-gradient(90deg, #f4f9ff, #ffffff);
  2213. }
  2214. .el-drawer__body {
  2215. flex: 1 1 auto;
  2216. min-height: 0;
  2217. height: auto;
  2218. overflow: hidden;
  2219. background: #f5f7fb;
  2220. }
  2221. }
  2222. .drawer-body {
  2223. height: 100%;
  2224. padding: 18px;
  2225. overflow-y: auto;
  2226. box-sizing: border-box;
  2227. }
  2228. .drawer-summary {
  2229. display: grid;
  2230. grid-template-columns: 78px 1fr;
  2231. gap: 14px;
  2232. margin-bottom: 18px;
  2233. padding: 16px;
  2234. border: 1px solid #cfe7ff;
  2235. border-radius: 8px;
  2236. background: linear-gradient(135deg, #f2f8ff, #ffffff 72%);
  2237. box-shadow: 0 10px 24px rgba(64, 158, 255, 0.1);
  2238. }
  2239. .summary-date {
  2240. height: 82px;
  2241. display: flex;
  2242. flex-direction: column;
  2243. align-items: center;
  2244. justify-content: center;
  2245. border-radius: 8px;
  2246. background: linear-gradient(180deg, #409eff, #1f78e8);
  2247. text-align: center;
  2248. box-shadow: 0 10px 20px rgba(64, 158, 255, 0.22);
  2249. span {
  2250. display: block;
  2251. color: rgba(255, 255, 255, 0.86);
  2252. font-size: 12px;
  2253. font-weight: 600;
  2254. }
  2255. strong {
  2256. display: block;
  2257. color: #fff;
  2258. font-size: 34px;
  2259. line-height: 38px;
  2260. }
  2261. }
  2262. .summary-info {
  2263. flex: 1;
  2264. min-width: 0;
  2265. .summary-title {
  2266. display: flex;
  2267. align-items: center;
  2268. justify-content: space-between;
  2269. gap: 8px;
  2270. color: #303133;
  2271. font-size: 16px;
  2272. font-weight: 600;
  2273. }
  2274. p {
  2275. margin: 8px 0 10px;
  2276. color: #606266;
  2277. font-size: 14px;
  2278. line-height: 20px;
  2279. }
  2280. }
  2281. .summary-tags {
  2282. display: flex;
  2283. flex-wrap: wrap;
  2284. gap: 6px;
  2285. }
  2286. .drawer-section {
  2287. margin-bottom: 18px;
  2288. padding: 16px;
  2289. border: 1px solid #e4edf7;
  2290. border-radius: 8px;
  2291. background: #fff;
  2292. box-shadow: 0 8px 18px rgba(31, 45, 61, 0.05);
  2293. }
  2294. .drawer-title {
  2295. display: flex;
  2296. align-items: center;
  2297. justify-content: space-between;
  2298. margin-bottom: 14px;
  2299. span {
  2300. position: relative;
  2301. padding-left: 10px;
  2302. color: #303133;
  2303. font-size: 17px;
  2304. font-weight: 600;
  2305. &::before {
  2306. content: '';
  2307. position: absolute;
  2308. left: 0;
  2309. top: 3px;
  2310. width: 3px;
  2311. height: 15px;
  2312. border-radius: 2px;
  2313. background: #409eff;
  2314. }
  2315. }
  2316. em {
  2317. color: #909399;
  2318. font-style: normal;
  2319. font-size: 14px;
  2320. }
  2321. }
  2322. .detail-timeline,
  2323. .plan-list {
  2324. display: flex;
  2325. flex-direction: column;
  2326. gap: 12px;
  2327. }
  2328. .detail-item {
  2329. display: grid;
  2330. grid-template-columns: 126px 1fr;
  2331. gap: 12px;
  2332. align-items: stretch;
  2333. }
  2334. .detail-time {
  2335. display: flex;
  2336. align-items: center;
  2337. justify-content: center;
  2338. min-height: 68px;
  2339. padding: 10px;
  2340. border: 1px solid #cfe7ff;
  2341. border-radius: 8px;
  2342. background: linear-gradient(180deg, #f3f8ff, #ffffff);
  2343. box-sizing: border-box;
  2344. text-align: center;
  2345. strong {
  2346. color: #1f4e86;
  2347. font-size: 16px;
  2348. line-height: 22px;
  2349. white-space: nowrap;
  2350. font-variant-numeric: tabular-nums;
  2351. }
  2352. }
  2353. .detail-content {
  2354. display: grid;
  2355. grid-template-columns: minmax(0, 1fr) auto;
  2356. align-items: center;
  2357. gap: 12px;
  2358. min-height: 68px;
  2359. padding: 12px 14px;
  2360. border: 1px solid #edf2f7;
  2361. border-left: 4px solid #409eff;
  2362. border-radius: 8px;
  2363. background: linear-gradient(180deg, #ffffff, #f8fbff);
  2364. box-sizing: border-box;
  2365. strong,
  2366. span {
  2367. display: block;
  2368. }
  2369. strong {
  2370. color: #303133;
  2371. font-size: 16px;
  2372. line-height: 22px;
  2373. }
  2374. span {
  2375. margin-top: 4px;
  2376. color: #909399;
  2377. font-size: 14px;
  2378. line-height: 20px;
  2379. }
  2380. }
  2381. .detail-meta {
  2382. grid-column: 1 / -1;
  2383. display: flex;
  2384. flex-wrap: wrap;
  2385. gap: 6px;
  2386. margin-top: 2px;
  2387. span {
  2388. margin-top: 0;
  2389. padding: 2px 8px;
  2390. border-radius: 10px;
  2391. background: #eef5ff;
  2392. color: #5b6f8f;
  2393. font-size: 12px;
  2394. line-height: 20px;
  2395. &.danger {
  2396. background: #fff1f0;
  2397. color: #ff4d4f;
  2398. }
  2399. }
  2400. }
  2401. .drawer-empty {
  2402. padding: 22px 0 10px;
  2403. color: #909399;
  2404. font-size: 14px;
  2405. text-align: center;
  2406. }
  2407. .plan-empty {
  2408. display: flex;
  2409. flex-direction: column;
  2410. align-items: center;
  2411. gap: 12px;
  2412. i {
  2413. color: #d8dee8;
  2414. font-size: 72px;
  2415. }
  2416. }
  2417. .plan-tooltip {
  2418. max-width: 360px;
  2419. line-height: 24px;
  2420. color: #303133;
  2421. word-break: break-all;
  2422. }
  2423. .plan-item {
  2424. display: block;
  2425. padding: 12px 16px;
  2426. border: 1px solid #edf2f7;
  2427. border-left: 4px solid #67c23a;
  2428. border-radius: 8px;
  2429. background: linear-gradient(180deg, #ffffff, #f8fbff);
  2430. }
  2431. .plan-main {
  2432. min-width: 0;
  2433. }
  2434. .plan-topline {
  2435. display: grid;
  2436. grid-template-columns: minmax(0, 1fr) max-content;
  2437. align-items: center;
  2438. gap: 12px;
  2439. margin-bottom: 6px;
  2440. strong {
  2441. color: #303133;
  2442. font-size: 16px;
  2443. line-height: 22px;
  2444. }
  2445. span {
  2446. margin-top: 0;
  2447. color: #8a97a8;
  2448. font-size: 14px;
  2449. line-height: 20px;
  2450. text-align: right;
  2451. }
  2452. }
  2453. .plan-name,
  2454. .plan-metrics,
  2455. .plan-topline strong,
  2456. .plan-topline span {
  2457. overflow: hidden;
  2458. text-overflow: ellipsis;
  2459. white-space: nowrap;
  2460. }
  2461. .plan-name {
  2462. display: block;
  2463. width: 100%;
  2464. margin: 0 0 8px;
  2465. color: #606266;
  2466. font-size: 14px;
  2467. line-height: 20px;
  2468. }
  2469. .plan-metrics {
  2470. display: flex;
  2471. align-items: center;
  2472. justify-content: flex-start;
  2473. gap: 14px;
  2474. color: #8a97a8;
  2475. font-size: 13px;
  2476. line-height: 20px;
  2477. font-variant-numeric: tabular-nums;
  2478. em {
  2479. font-style: normal;
  2480. }
  2481. }
  2482. @keyframes conflictFlash {
  2483. 0%,
  2484. 100% {
  2485. box-shadow: inset 0 0 0 1px rgba(245, 108, 108, 0.25);
  2486. }
  2487. 50% {
  2488. box-shadow: inset 0 0 0 2px rgba(245, 108, 108, 0.75);
  2489. }
  2490. }
  2491. .notification-card {
  2492. .card-content {
  2493. height: 100%;
  2494. min-height: 0;
  2495. display: flex;
  2496. flex-direction: column;
  2497. }
  2498. ::v-deep .el-card__body {
  2499. padding-top: 0;
  2500. }
  2501. ::v-deep .el-tabs__item {
  2502. font-size: 15px;
  2503. font-weight: bold;
  2504. color: #333;
  2505. }
  2506. }
  2507. @media (max-width: 1440px) {
  2508. .notification-col {
  2509. width: 50%;
  2510. }
  2511. .calendar-col {
  2512. width: 50%;
  2513. }
  2514. .home-container {
  2515. .work-calendar {
  2516. .calendar-view-title {
  2517. font-size: 16px;
  2518. .calendar-title-tools {
  2519. .calendar-type-switch {
  2520. ::v-deep .el-radio-button__inner {
  2521. height: 26px;
  2522. padding: 6px 9px;
  2523. font-size: 11px;
  2524. line-height: 12px;
  2525. }
  2526. }
  2527. }
  2528. }
  2529. .calendar-cell {
  2530. min-height: 92px;
  2531. padding: 6px 7px 6px 9px;
  2532. }
  2533. }
  2534. }
  2535. }
  2536. @media (max-width: 1200px) {
  2537. .home-container {
  2538. .work-calendar {
  2539. .calendar-view-title {
  2540. gap: 6px;
  2541. }
  2542. .calendar-title-tools {
  2543. justify-content: flex-end;
  2544. .calendar-type-switch {
  2545. ::v-deep .el-radio-button__inner {
  2546. padding: 6px 7px;
  2547. }
  2548. }
  2549. }
  2550. .calendar-week span {
  2551. height: 28px;
  2552. line-height: 28px;
  2553. }
  2554. .calendar-cell {
  2555. min-height: 90px;
  2556. padding: 6px 6px 6px 8px;
  2557. }
  2558. }
  2559. }
  2560. }
  2561. @media (max-width: 768px) {
  2562. ::v-deep .calendar-day-drawer {
  2563. width: 92% !important;
  2564. }
  2565. .drawer-summary,
  2566. .detail-item {
  2567. grid-template-columns: 1fr;
  2568. }
  2569. }
  2570. /* 通知公告样式 */
  2571. .notification-list {
  2572. // margin-top: 16px;
  2573. flex: 1 1 auto;
  2574. min-height: 0;
  2575. overflow-y: auto;
  2576. overflow-x: hidden;
  2577. padding-right: 8px;
  2578. &::-webkit-scrollbar {
  2579. width: 6px;
  2580. }
  2581. &::-webkit-scrollbar-track {
  2582. background: #f1f1f1;
  2583. border-radius: 3px;
  2584. }
  2585. &::-webkit-scrollbar-thumb {
  2586. background: #c1c1c1;
  2587. border-radius: 3px;
  2588. }
  2589. &::-webkit-scrollbar-thumb:hover {
  2590. background: #a8a8a8;
  2591. }
  2592. }
  2593. .notification-item {
  2594. display: flex;
  2595. align-items: center;
  2596. padding: 12px 0;
  2597. border-bottom: 1px solid #f0f0f0;
  2598. &:last-child {
  2599. border-bottom: none;
  2600. }
  2601. }
  2602. .notification-dot {
  2603. width: 10px;
  2604. height: 10px;
  2605. background-color: #a6ccff;
  2606. border-radius: 50%;
  2607. margin-right: 12px;
  2608. margin-top: 5px;
  2609. flex-shrink: 0;
  2610. }
  2611. .notification-content {
  2612. flex: 1;
  2613. min-width: 0;
  2614. cursor: pointer;
  2615. color: #333;
  2616. &:hover {
  2617. color: #1890ff;
  2618. }
  2619. }
  2620. .notification-header {
  2621. display: flex;
  2622. align-items: center;
  2623. margin-bottom: 4px;
  2624. }
  2625. .notification-title {
  2626. font-size: 15px;
  2627. font-weight: 500;
  2628. // color: #333;
  2629. margin-right: 12px;
  2630. white-space: nowrap;
  2631. overflow: hidden;
  2632. text-overflow: ellipsis;
  2633. flex-shrink: 1;
  2634. max-width: 200px;
  2635. }
  2636. .notification-info {
  2637. font-size: 12px;
  2638. // color: #333;
  2639. white-space: nowrap;
  2640. overflow: hidden;
  2641. text-overflow: ellipsis;
  2642. }
  2643. .notification-text {
  2644. font-size: 14px;
  2645. // color: #333;
  2646. line-height: 1.4;
  2647. flex: 1;
  2648. min-width: 0;
  2649. white-space: nowrap;
  2650. overflow: hidden;
  2651. text-overflow: ellipsis;
  2652. }
  2653. .notification-images {
  2654. margin-top: 8px;
  2655. }
  2656. .image-url {
  2657. font-size: 12px;
  2658. color: #1890ff;
  2659. cursor: pointer;
  2660. margin-bottom: 4px;
  2661. white-space: nowrap;
  2662. overflow: hidden;
  2663. text-overflow: ellipsis;
  2664. &:hover {
  2665. text-decoration: underline;
  2666. }
  2667. }
  2668. .notification-date {
  2669. font-size: 13px;
  2670. // color: #333;
  2671. margin-left: 12px;
  2672. flex-shrink: 0;
  2673. }
  2674. .notification-more {
  2675. text-align: right;
  2676. margin-top: 12px;
  2677. font-size: 15px;
  2678. color: #1890ff;
  2679. cursor: pointer;
  2680. &:hover {
  2681. text-decoration: underline;
  2682. }
  2683. }
  2684. </style>
  2685. <style lang="scss">
  2686. body .calendar-day-tooltip {
  2687. max-width: min(360px, calc(100vw - 32px));
  2688. max-height: calc(100vh - 32px);
  2689. padding: 0;
  2690. border: 1px solid #d8e6f7;
  2691. border-radius: 8px;
  2692. box-shadow: 0 16px 36px rgba(31, 45, 61, 0.18);
  2693. overflow: auto;
  2694. .popper__arrow {
  2695. border-top-color: #d8e6f7 !important;
  2696. border-bottom-color: #d8e6f7 !important;
  2697. }
  2698. }
  2699. body .calendar-day-tooltip .calendar-tooltip-content {
  2700. min-width: 270px;
  2701. max-width: 340px;
  2702. padding: 12px 14px;
  2703. color: #303133;
  2704. background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);
  2705. border-radius: 8px;
  2706. }
  2707. body .calendar-day-tooltip .tooltip-head {
  2708. display: flex;
  2709. align-items: center;
  2710. justify-content: space-between;
  2711. gap: 12px;
  2712. margin-bottom: 10px;
  2713. padding-bottom: 8px;
  2714. border-bottom: 1px solid #e6eef8;
  2715. strong {
  2716. color: #1f3d66;
  2717. font-size: 15px;
  2718. line-height: 20px;
  2719. font-weight: 700;
  2720. }
  2721. span {
  2722. height: 20px;
  2723. padding: 0 8px;
  2724. border-radius: 10px;
  2725. background: #fff1f0;
  2726. color: #f5222d;
  2727. font-size: 12px;
  2728. font-weight: 700;
  2729. line-height: 20px;
  2730. }
  2731. }
  2732. body .calendar-day-tooltip .tooltip-row {
  2733. display: grid;
  2734. grid-template-columns: 72px minmax(0, 1fr);
  2735. gap: 10px;
  2736. align-items: start;
  2737. margin-top: 7px;
  2738. padding: 6px 8px;
  2739. border-radius: 6px;
  2740. background: rgba(236, 246, 255, 0.68);
  2741. font-size: 13px;
  2742. line-height: 20px;
  2743. span {
  2744. color: #7b8794;
  2745. }
  2746. strong {
  2747. color: #2f3a4a;
  2748. font-weight: 600;
  2749. word-break: break-word;
  2750. }
  2751. }
  2752. body .calendar-day-tooltip .tooltip-stats {
  2753. display: flex;
  2754. flex-wrap: wrap;
  2755. gap: 8px;
  2756. margin-top: 12px;
  2757. span {
  2758. padding: 3px 9px;
  2759. border-radius: 12px;
  2760. background: #edf5ff;
  2761. color: #1f66c2;
  2762. font-size: 12px;
  2763. font-weight: 700;
  2764. line-height: 18px;
  2765. }
  2766. }
  2767. </style>