| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349 |
- <template>
- <div class="app-container">
- <!-- 系统基础信息卡片 -->
- <el-card class="system-info-card">
- <div slot="header" class="card-header">
- <span class="title">
- <i class="el-icon-sunny"></i>
- {{ systemInfo.systemName || '智慧照明系统' }}
- </span>
- <el-tag :type="systemStatus === '1' ? 'success' : 'danger'" effect="dark">
- {{ systemStatus === '1' ? '正常' : '异常' }}
- </el-tag>
- </div>
- <el-row :gutter="20" style="position: relative;">
- <el-col :span="6">
- <div class="info-group">
- <div class="group-title">系统信息</div>
- <div class="info-item">
- <label>系统代码:</label>
- <span class="code">{{ systemInfo.systemCode }}</span>
- </div>
- <div class="info-item">
- <label>系统名称:</label>
- <span>{{ systemInfo.systemName }}</span>
- </div>
- <div class="info-item">
- <label>系统简称:</label>
- <span>{{ systemInfo.shortName || '-' }}</span>
- </div>
- <div class="info-item">
- <label>模型代码:</label>
- <span class="code">{{ systemInfo.modelCode }}</span>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="info-group">
- <div class="group-title">厂商信息</div>
- <div class="info-item">
- <label>对接厂商:</label>
- <span>{{ systemInfo.manFacturer || '-' }}</span>
- </div>
- <div class="info-item">
- <label>联系人:</label>
- <span>{{ systemInfo.contactPerson || '-' }}</span>
- </div>
- <div class="info-item">
- <label>联系电话:</label>
- <span>{{ systemInfo.contactNumber || '-' }}</span>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="info-group">
- <div class="group-title">维护信息</div>
- <div class="info-item">
- <label>维护人:</label>
- <span>{{ systemInfo.maintainerPerson || '-' }}</span>
- </div>
- <div class="info-item">
- <label>维护电话:</label>
- <span>{{ systemInfo.maintainerNumber || '-' }}</span>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="info-group">
- <div class="group-title">系统统计</div>
- <div class="info-item">
- <label>项目数量:</label>
- <span class="stat-number">{{ projectList.length }}</span>
- </div>
- <div class="info-item">
- <label>分组数量:</label>
- <span class="stat-number">{{ subsetList.length }}</span>
- </div>
- <div class="info-item">
- <label>设备总数:</label>
- <span class="stat-number">{{ deviceStats.total }}</span>
- </div>
- <div style="text-align: right; margin-top: 10px;">
- <el-button
- type="primary"
- size="mini"
- icon="el-icon-edit"
- @click="handleEditSystem">
- 编辑
- </el-button>
- </div>
- </div>
- </el-col>
- </el-row>
- </el-card>
- <!-- 系统详情标签页 -->
- <el-card class="detail-card">
- <el-tabs v-model="activeTab" @tab-click="handleTabClick">
- <!-- 系统总览标签页 -->
- <el-tab-pane label="系统总览" name="overview">
- <div class="tab-content">
- <!-- 快速操作面板 -->
- <div class="quick-actions">
- <h4 class="section-title">
- <i class="el-icon-s-operation"></i>
- 快速操作
- </h4>
- <el-row :gutter="20">
- <el-col :span="6" v-for="action in quickActions" :key="action.key">
- <el-button
- class="action-btn"
- :icon="action.icon"
- @click="handleQuickAction(action)"
- :loading="action.loading">
- {{ action.name }}
- </el-button>
- </el-col>
- </el-row>
- </div>
- <!-- 实时监控面板 -->
- <div class="monitor-panel">
- <h4 class="section-title">
- <i class="el-icon-monitor"></i>
- 实时监控
- </h4>
- <el-row :gutter="20">
- <el-col :xl="4" :lg="4" :md="8" :sm="12" :xs="24">
- <div class="monitor-card">
- <div class="monitor-icon online">
- <i class="el-icon-circle-check"></i>
- </div>
- <div class="monitor-info">
- <div class="monitor-value">{{ deviceStats.online }}</div>
- <div class="monitor-label">在线设备</div>
- </div>
- </div>
- </el-col>
- <el-col :xl="4" :lg="4" :md="8" :sm="12" :xs="24">
- <div class="monitor-card">
- <div class="monitor-icon offline">
- <i class="el-icon-circle-close"></i>
- </div>
- <div class="monitor-info">
- <div class="monitor-value">{{ deviceStats.offline }}</div>
- <div class="monitor-label">离线设备</div>
- </div>
- </div>
- </el-col>
- <el-col :xl="5" :lg="5" :md="8" :sm="12" :xs="24">
- <div class="monitor-card">
- <div class="monitor-icon lamps">
- <i class="el-icon-light"></i>
- </div>
- <div class="monitor-info">
- <div class="monitor-value">{{ lampStats.on }}</div>
- <div class="monitor-label">开启灯组</div>
- </div>
- </div>
- </el-col>
- <el-col :xl="5" :lg="5" :md="8" :sm="12" :xs="24">
- <div class="monitor-card">
- <div class="monitor-icon concentrator">
- <i class="el-icon-connection"></i>
- </div>
- <div class="monitor-info">
- <div class="monitor-value">{{ deviceStats.concentrators }}</div>
- <div class="monitor-label">集中器数量</div>
- </div>
- </div>
- </el-col>
- <el-col :xl="6" :lg="6" :md="8" :sm="12" :xs="24">
- <div class="monitor-card">
- <div class="monitor-icon power">
- <i class="el-icon-s-data"></i>
- </div>
- <div class="monitor-info">
- <div class="monitor-value">{{ totalPower.toFixed(2) }}W</div>
- <div class="monitor-label">总功率</div>
- </div>
- </div>
- </el-col>
- </el-row>
- </div>
- <!-- 集中器设备表格 -->
- <div class="concentrator-panel">
- <h4 class="section-title">
- <i class="el-icon-connection"></i>
- 集中器设备
- </h4>
- <el-table
- :data="concentratorList"
- border
- stripe
- v-loading="concentratorLoading"
- @row-click="handleConcentratorClick"
- ref="concentratorTable">
- <el-table-column type="expand">
- <template slot-scope="props">
- <div class="device-detail" @click.stop>
- <el-tabs v-model="props.row.detailTab">
- <!-- 基础属性标签页 -->
- <el-tab-pane label="基础属性" name="base">
- <el-descriptions :column="2" border size="small" class="concentrator-descriptions">
- <el-descriptions-item label="设备SN号">
- {{ getConcentratorAttr(props.row.deviceCode, 'deviceUid', 'Base') || '-' }}
- </el-descriptions-item>
- <el-descriptions-item label="设备型号ID">
- {{ getConcentratorAttr(props.row.deviceCode, 'deviceModelId', 'Base') || '-' }}
- </el-descriptions-item>
- <el-descriptions-item label="设备状态">
- <el-tag size="small" :type="getConcentratorAttr(props.row.deviceCode, 'deviceStatus', 'Base') === '1' ? 'success' : 'danger'">
- {{ getConcentratorAttr(props.row.deviceCode, 'deviceStatus', 'Base') === '1' ? '正常' : '异常' }}
- </el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="信号强度">
- {{ getConcentratorAttr(props.row.deviceCode, 'csq', 'Base') || '-' }}
- </el-descriptions-item>
- </el-descriptions>
- <!-- 设备图片 -->
- <div v-if="getConcentratorAttr(props.row.deviceCode, 'imageUrl', 'Base')" class="device-image" style="margin-top: 20px;">
- <h5>设备图片</h5>
- <el-image
- style="width: 200px; height: 150px"
- :src="getConcentratorAttr(props.row.deviceCode, 'imageUrl', 'Base')"
- :preview-src-list="[getConcentratorAttr(props.row.deviceCode, 'imageUrl', 'Base')]"
- fit="cover">
- <div slot="error" class="image-slot">
- <i class="el-icon-picture-outline"></i>
- </div>
- </el-image>
- </div>
- </el-tab-pane>
- <!-- 电力属性标签页 -->
- <el-tab-pane label="电力属性" name="power">
- <div class="concentrator-detail">
- <!-- 功率参数 -->
- <div class="param-group">
- <div class="group-header">
- <span class="group-title">功率参数</span>
- </div>
- <el-row :gutter="20">
- <el-col :span="12">
- <div class="param-card">
- <div class="param-label">有功功率</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'kwh') || '0.0' }}</span>
- <span class="unit">kW</span>
- </div>
- </div>
- </el-col>
- <el-col :span="12">
- <div class="param-card">
- <div class="param-label">无功功率</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'kvarh') || '0.0' }}</span>
- <span class="unit">kVar</span>
- </div>
- </div>
- </el-col>
- </el-row>
- </div>
- <!-- 三相电压 -->
- <div class="param-group">
- <div class="group-header">
- <span class="group-title">三相电压</span>
- <el-tag size="mini" :type="getVoltageStatus(props.row.deviceCode).type">
- {{ getVoltageStatus(props.row.deviceCode).text }}
- </el-tag>
- </div>
- <el-row :gutter="20">
- <el-col :span="8">
- <div class="param-card phase-a" :class="getVoltageClass(getConcentratorAttr(props.row.deviceCode, 'ua'))">
- <div class="phase-label">A相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'ua') || '-' }}</span>
- <span class="unit">V</span>
- </div>
- <div class="voltage-indicator">
- <span class="normal-range">正常: 198-242V</span>
- </div>
- </div>
- </el-col>
- <el-col :span="8">
- <div class="param-card phase-b" :class="getVoltageClass(getConcentratorAttr(props.row.deviceCode, 'ub'))">
- <div class="phase-label">B相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'ub') || '-' }}</span>
- <span class="unit">V</span>
- </div>
- <div class="voltage-indicator">
- <span class="normal-range">正常: 198-242V</span>
- </div>
- </div>
- </el-col>
- <el-col :span="8">
- <div class="param-card phase-c" :class="getVoltageClass(getConcentratorAttr(props.row.deviceCode, 'uc'))">
- <div class="phase-label">C相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'uc') || '-' }}</span>
- <span class="unit">V</span>
- </div>
- <div class="voltage-indicator">
- <span class="normal-range">正常: 198-242V</span>
- </div>
- </div>
- </el-col>
- </el-row>
- </div>
- <!-- 三相电流 -->
- <div class="param-group">
- <div class="group-header">
- <span class="group-title">三相电流</span>
- </div>
- <el-row :gutter="20">
- <el-col :span="8">
- <div class="param-card phase-a">
- <div class="phase-label">A相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'ia') || '-' }}</span>
- <span class="unit">A</span>
- </div>
- </div>
- </el-col>
- <el-col :span="8">
- <div class="param-card phase-b">
- <div class="phase-label">B相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'ib') || '-' }}</span>
- <span class="unit">A</span>
- </div>
- </div>
- </el-col>
- <el-col :span="8">
- <div class="param-card phase-c">
- <div class="phase-label">C相</div>
- <div class="param-value">
- <span class="value">{{ getConcentratorAttr(props.row.deviceCode, 'ic') || '-' }}</span>
- <span class="unit">A</span>
- </div>
- </div>
- </el-col>
- </el-row>
- </div>
- <!-- 更新时间 -->
- <div class="update-time">
- <i class="el-icon-time"></i>
- 最后更新:{{ getConcentratorUpdateTime(props.row.deviceCode) || '暂无数据' }}
- </div>
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="deviceCode" label="设备代码" width="180"></el-table-column>
- <el-table-column prop="deviceName" label="设备名称"></el-table-column>
- <el-table-column prop="location" label="安装位置"></el-table-column>
- <el-table-column label="设备状态" width="100" align="center">
- <template slot-scope="scope">
- <el-tag :type="scope.row.deviceStatus === 1 ? 'success' : 'info'" size="small">
- {{ scope.row.deviceStatus === 1 ? '在线' : '离线' }}
- </el-tag>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </el-tab-pane>
- <!-- 系统属性标签页 -->
- <el-tab-pane label="系统属性" name="attributes">
- <div class="tab-content">
- <!-- 协议属性 -->
- <div class="attr-section" v-if="protocolAttrs.length > 0">
- <h4 class="section-title">
- <i class="el-icon-connection"></i>
- 协议信息
- </h4>
- <el-table :data="protocolAttrs" border stripe>
- <el-table-column prop="attrName" label="属性名称" width="200"></el-table-column>
- <el-table-column label="属性值">
- <template slot-scope="scope">
- <span v-if="scope.row.attrKey === 'url'" class="url-text">
- {{ scope.row.attrValue }}
- </span>
- <span v-else-if="scope.row.attrKey === 'password'" style="display: inline-flex; align-items: center;">
- <span>{{ passwordVisible ? scope.row.attrValue : '••••••••' }}</span>
- <i
- class="el-icon-view"
- @click.stop="passwordVisible = !passwordVisible"
- :title="passwordVisible ? '隐藏密码' : '显示密码'"
- :style="{
- marginLeft: '10px',
- cursor: 'pointer',
- fontSize: '16px',
- color: passwordVisible ? '#409EFF' : '#C0C4CC'
- }"
- ></i>
- </span>
- <span v-else>{{ scope.row.attrValue || '-' }}</span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <!-- 状态属性 -->
- <div class="attr-section" v-if="stateAttrs.length > 0">
- <h4 class="section-title">
- <i class="el-icon-info"></i>
- 状态信息
- </h4>
- <el-table :data="stateAttrs" border stripe>
- <el-table-column prop="attrName" label="属性名称" width="200"></el-table-column>
- <el-table-column label="属性值">
- <template slot-scope="scope">
- <el-tag v-if="scope.row.attrKey === 'interfaceStatus'"
- :type="scope.row.attrValue === '1' ? 'success' : 'danger'">
- {{ scope.row.attrValueName }}
- </el-tag>
- <span v-else>{{ scope.row.attrValueName || scope.row.attrValue || '-' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="updateTime" label="更新时间" width="180"></el-table-column>
- </el-table>
- </div>
- <!-- 基础属性 -->
- <div class="attr-section" v-if="baseAttrs.length > 0">
- <h4 class="section-title">
- <i class="el-icon-folder"></i>
- 基础信息
- </h4>
- <!-- 项目列表 -->
- <div v-if="projectList.length > 0" class="project-section">
- <h5>项目列表</h5>
- <el-table :data="projectList" border stripe size="small">
- <el-table-column prop="projectName" label="项目名称"></el-table-column>
- <el-table-column prop="projectAddress" label="项目地址"></el-table-column>
- <el-table-column prop="projectDeviceNum" label="设备数量" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small">{{ scope.row.projectDeviceNum }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="projectSubsetNum" label="分组数量" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small" type="info">{{ scope.row.projectSubsetNum }}</el-tag>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <!-- 分组列表 -->
- <div v-if="subsetList.length > 0" class="project-section">
- <h5>分组列表</h5>
- <el-table :data="subsetList" border stripe size="small">
- <el-table-column prop="subsetName" label="分组名称"></el-table-column>
- <el-table-column prop="subsetId" label="分组ID"></el-table-column>
- <el-table-column prop="subsetDeviceNum" label="设备数量" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small" :type="scope.row.subsetDeviceNum > 0 ? 'success' : 'info'">
- {{ scope.row.subsetDeviceNum }}
- </el-tag>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </div>
- </el-tab-pane>
- <!-- 系统能力标签页 -->
- <el-tab-pane label="系统能力" name="abilities">
- <div class="tab-content">
- <el-table :data="systemAbilities" border stripe v-loading="abilityLoading">
- <el-table-column prop="abilityName" label="能力名称" width="200"></el-table-column>
- <el-table-column prop="abilityKey" label="能力标识" width="200"></el-table-column>
- <el-table-column prop="abilityDesc" label="能力描述"></el-table-column>
- <el-table-column label="操作" width="200" align="center">
- <template slot-scope="scope">
- <!-- 根据参数类型展示不同的操作界面 -->
- <div v-if="getParamType(scope.row.paramDefinition) === 'Options'" style="display: inline-block;">
- <!-- Options类型:显示多个按钮 -->
- <el-button-group>
- <el-button
- v-for="option in parseOptions(scope.row.paramDefinition)"
- :key="option.value"
- size="mini"
- type="primary"
- @click="executeSystemAbilityWithParam(scope.row, option.value)"
- :loading="scope.row.executing && scope.row.executingValue === option.value">
- {{ option.key }}
- </el-button>
- </el-button-group>
- </div>
- <el-button
- v-else
- type="primary"
- size="mini"
- icon="el-icon-caret-right"
- @click="handleSystemAbilityClick(scope.row)"
- :loading="scope.row.executing">
- 执行
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </el-tab-pane>
- <!-- 智慧灯杆标签页 -->
- <el-tab-pane label="智慧灯杆" name="devices">
- <div class="tab-content">
- <!-- 设备统计 -->
- <div class="device-stats">
- <el-row :gutter="20">
- <el-col :span="6">
- <div class="stat-item">
- <div class="stat-value">{{ lampPoleList.length }}</div>
- <div class="stat-label">灯杆总数</div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="stat-item online">
- <div class="stat-value">{{ lampPoleList.filter(p => p.deviceStatus === 1).length }}</div>
- <div class="stat-label">在线灯杆</div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="stat-item lamps">
- <div class="stat-value">{{ totalLampCount }}</div>
- <div class="stat-label">灯组总数</div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="stat-item controllers">
- <div class="stat-value">{{ onlineLampCount }}</div>
- <div class="stat-label">在线灯组</div>
- </div>
- </el-col>
- </el-row>
- </div>
- <!-- 智慧灯杆列表(两层嵌套:灯杆->灯组->详情) -->
- <el-table
- :data="lampPoleList"
- border
- stripe
- v-loading="lampPoleLoading"
- row-key="deviceCode"
- @row-click="handleLampPoleClick"
- ref="lampPoleTable"
- class="lamp-pole-main-table">
- <!-- 灯杆展开:显示灯组列表 -->
- <el-table-column type="expand">
- <template slot-scope="props">
- <div class="lamp-pole-detail" @click.stop>
- <!-- 灯组列表表格 -->
- <el-table
- :data="getLampGroupList(props.row.deviceCode)"
- border
- stripe
- size="small"
- row-key="deviceCode"
- @row-click="(row, column, event) => handleLampGroupClick(row, column, event, props.$index)"
- :ref="`lampGroupTable_${props.$index}`"
- class="lamp-group-table">
- <!-- 灯组展开:显示灯组属性和能力 -->
- <el-table-column type="expand" width="40">
- <template slot-scope="scope">
- <div class="lamp-group-detail" @click.stop>
- <el-tabs v-model="scope.row.detailTab">
- <!-- 灯组属性标签页 -->
- <el-tab-pane label="灯组属性" name="attrs">
- <!-- 状态属性 -->
- <div v-if="getLampGroupModelAttrs(scope.row.deviceCode, 'State').length > 0">
- <h5 class="attr-group-title">状态属性</h5>
- <el-descriptions :column="2" border size="small" class="lamp-group-descriptions">
- <el-descriptions-item
- v-for="attr in getLampGroupModelAttrs(scope.row.deviceCode, 'State')"
- :key="attr.attrKey"
- :label="attr.attrName">
- <template v-if="attr.attrValueType === 'Enum'">
- <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) === '1' ? 'success' : 'info'">
- {{ getLampGroupAttrEnumName(scope.row.deviceCode, attr.attrKey, attr.valueEnums) }}
- </el-tag>
- </template>
- <template v-else>
- {{ getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) || '-' }}
- <span v-if="attr.attrUnit">{{ attr.attrUnit }}</span>
- </template>
- </el-descriptions-item>
- </el-descriptions>
- </div>
- <!-- 计量属性 -->
- <div v-if="getLampGroupModelAttrs(scope.row.deviceCode, 'Measure').length > 0" style="margin-top: 20px;">
- <h5 class="attr-group-title">计量属性</h5>
- <el-descriptions :column="2" border size="small" class="lamp-group-descriptions">
- <el-descriptions-item
- v-for="attr in getLampGroupModelAttrs(scope.row.deviceCode, 'Measure')"
- :key="attr.attrKey"
- :label="attr.attrName">
- {{ getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) || '-' }}
- <span v-if="attr.attrUnit">{{ attr.attrUnit }}</span>
- </el-descriptions-item>
- </el-descriptions>
- </div>
- <!-- 基础属性 -->
- <div v-if="getLampGroupModelAttrs(scope.row.deviceCode, 'Base').length > 0" style="margin-top: 20px;">
- <h5 class="attr-group-title">基础属性</h5>
- <el-descriptions :column="2" border size="small" class="lamp-group-descriptions">
- <el-descriptions-item
- v-for="attr in getLampGroupModelAttrs(scope.row.deviceCode, 'Base')"
- :key="attr.attrKey"
- :label="attr.attrName">
- <template v-if="attr.attrValueType === 'Enum'">
- <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) === '0' ? 'success' : 'danger'">
- {{ getLampGroupAttrEnumName(scope.row.deviceCode, attr.attrKey, attr.valueEnums) }}
- </el-tag>
- </template>
- <template v-else-if="attr.attrKey === 'imageUrl'">
- <el-image
- v-if="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey)"
- style="width: 100px; height: 75px"
- :src="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey)"
- :preview-src-list="[getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey)]"
- fit="cover">
- </el-image>
- <span v-else>-</span>
- </template>
- <template v-else>
- {{ getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) || '-' }}
- <span v-if="attr.attrUnit">{{ attr.attrUnit }}</span>
- </template>
- </el-descriptions-item>
- </el-descriptions>
- </div>
- <!-- 更新时间 -->
- <div class="update-time" style="margin-top: 15px;">
- <i class="el-icon-time"></i>
- 最后更新:{{ getLampGroupUpdateTime(scope.row.deviceCode) || '暂无数据' }}
- </div>
- </el-tab-pane>
- <!-- 灯组能力标签页 -->
- <el-tab-pane label="控制操作" name="abilities">
- <div class="ability-panel">
- <template v-if="lampGroupAbilities[scope.row.deviceCode] && lampGroupAbilities[scope.row.deviceCode].length > 0">
- <el-row :gutter="15">
- <el-col :span="8" v-for="ability in lampGroupAbilities[scope.row.deviceCode]" :key="ability.abilityKey">
- <div class="ability-card">
- <div class="ability-header">
- <i class="el-icon-magic-stick"></i>
- <span>{{ ability.abilityName }}</span>
- </div>
- <div class="ability-desc">{{ ability.abilityDesc }}</div>
- <!-- Options类型:多个按钮 -->
- <template v-if="getParamType(ability.paramDefinition) === 'Options'">
- <!-- 开关控制特殊处理:根据当前状态切换按钮样式 -->
- <template v-if="ability.abilityKey === 'lightControl'">
- <div class="light-control-buttons">
- <el-button
- v-for="option in parseOptions(ability.paramDefinition)"
- :key="option.value"
- :type="getLightControlButtonType(scope.row.deviceCode, option.value)"
- size="small"
- :icon="option.value === '1' ? 'el-icon-sunny' : 'el-icon-moon'"
- @click="executeLampGroupAbilityWithParam(scope.row, ability, option.value)"
- :loading="ability.executing && ability.executingValue === option.value">
- {{ option.key }}
- </el-button>
- </div>
- </template>
- <!-- 其他Options类型:显示所有按钮 -->
- <el-button-group v-else class="ability-buttons">
- <el-button
- v-for="option in parseOptions(ability.paramDefinition)"
- :key="option.value"
- size="small"
- @click="executeLampGroupAbilityWithParam(scope.row, ability, option.value)"
- :loading="ability.executing && ability.executingValue === option.value">
- {{ option.key }}
- </el-button>
- </el-button-group>
- </template>
- <!-- 其他类型:单个按钮 -->
- <el-button
- v-else
- type="primary"
- size="small"
- @click="handleLampGroupAbilityClick(scope.row, ability)"
- :loading="ability.executing"
- style="width: 100%; margin-top: 10px;">
- 执行
- </el-button>
- </div>
- </el-col>
- </el-row>
- </template>
- <el-empty v-else description="暂无可用操作" :image-size="80"></el-empty>
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
- </template>
- </el-table-column>
- <!-- 设备名称列(新增) -->
- <el-table-column label="设备名称" width="120">
- <template slot-scope="scope">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'deviceName') || '-' }}
- </template>
- </el-table-column>
- <el-table-column prop="deviceCode" label="灯组代码" min-width="180"></el-table-column>
- <el-table-column label="设备SN" width="110">
- <template slot-scope="scope">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'deviceUid') || '-' }}
- </template>
- </el-table-column>
- <el-table-column label="开关状态" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, 'Switch') === '1' ? 'success' : 'info'">
- <i :class="getLampGroupAttrValue(scope.row.deviceCode, 'Switch') === '1' ? 'el-icon-sunny' : 'el-icon-moon'"></i>
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'Switch') === '1' ? '开启' : '关闭' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="设备状态" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, 'deviceStatus') === '1' ? 'success' : 'danger'">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'deviceStatus') === '1' ? '正常' : '异常' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="功率" width="90" align="center">
- <template slot-scope="scope">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'power') || '0' }}W
- </template>
- </el-table-column>
- <el-table-column label="电压" width="90" align="center">
- <template slot-scope="scope">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'voltage') || '0' }}V
- </template>
- </el-table-column>
- <el-table-column label="电流" width="90" align="center">
- <template slot-scope="scope">
- {{ getLampGroupAttrValue(scope.row.deviceCode, 'current') || '0' }}A
- </template>
- </el-table-column>
- <el-table-column label="操作" width="100" align="center">
- <template slot-scope="scope">
- <el-dropdown trigger="click" @command="handleLampGroupAbilityCommand">
- <el-button type="text" size="mini" @click.stop="loadLampGroupAbilities(scope.row)">
- 操作<i class="el-icon-arrow-down el-icon--right"></i>
- </el-button>
- <el-dropdown-menu slot="dropdown">
- <template v-if="currentLampGroupAbilities && currentLampGroupAbilities.length > 0">
- <el-dropdown-item
- v-for="ability in currentLampGroupAbilities"
- :key="ability.abilityKey"
- :command="{ ability: ability, device: scope.row }"
- >
- <i class="el-icon-caret-right" style="margin-right: 5px;"></i>
- {{ ability.abilityName }}
- <i v-if="getParamType(ability.paramDefinition) === 'Options'"
- class="el-icon-arrow-right"
- style="float: right; margin-left: 10px;"></i>
- </el-dropdown-item>
- </template>
- <el-dropdown-item v-else>
- 无可用操作
- </el-dropdown-item>
- </el-dropdown-menu>
- </el-dropdown>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="deviceCode" label="灯杆代码" width="140"></el-table-column>
- <el-table-column prop="deviceName" label="灯杆名称" width="110"></el-table-column>
- <el-table-column prop="location" label="安装位置" min-width="120"></el-table-column>
- <el-table-column label="灯组数量" width="90" align="center">
- <template slot-scope="scope">
- <el-tag type="primary" size="small">{{ getLampGroupList(scope.row.deviceCode).length }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="在线灯组" width="90" align="center">
- <template slot-scope="scope">
- <el-tag type="success" size="small">{{ getOnlineLampGroupCount(scope.row.deviceCode) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="开启灯组" width="90" align="center">
- <template slot-scope="scope">
- <el-tag type="success" size="small">{{ getOnSwitchLampGroupCount(scope.row.deviceCode) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="设备状态" width="90" align="center">
- <template slot-scope="scope">
- <el-tag :type="scope.row.deviceStatus === 1 ? 'success' : 'info'" size="small">
- {{ scope.row.deviceStatus === 1 ? '在线' : '离线' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="100" align="center">
- <template slot-scope="scope">
- <el-dropdown trigger="click" @command="handleLampPoleAbilityCommand">
- <el-button type="text" size="mini" @click.stop="loadLampPoleAbilities(scope.row)">
- 操作<i class="el-icon-arrow-down el-icon--right"></i>
- </el-button>
- <el-dropdown-menu slot="dropdown">
- <template v-if="currentLampPoleAbilities && currentLampPoleAbilities.length > 0">
- <el-dropdown-item
- v-for="ability in currentLampPoleAbilities"
- :key="ability.abilityKey"
- :command="{ ability: ability, device: scope.row }"
- >
- <i class="el-icon-caret-right" style="margin-right: 5px;"></i>
- {{ ability.abilityName }}
- <i v-if="getParamType(ability.paramDefinition) === 'Options'"
- class="el-icon-arrow-right"
- style="float: right; margin-left: 10px;"></i>
- </el-dropdown-item>
- </template>
- <el-dropdown-item v-else disabled>
- 无可用操作
- </el-dropdown-item>
- </el-dropdown-menu>
- </el-dropdown>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </el-tab-pane>
- <!-- 调用日志标签页 -->
- <el-tab-pane label="调用日志" name="callLogs">
- <div class="tab-content">
- <el-form :inline="true" :model="callLogQuery" class="log-filter">
- <el-form-item label="时间范围">
- <el-date-picker
- v-model="callLogQuery.dateRange"
- type="datetimerange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="yyyy-MM-dd HH:mm:ss">
- </el-date-picker>
- </el-form-item>
- <el-form-item label="能力标识">
- <el-input v-model="callLogQuery.abilityKey" placeholder="请输入能力标识" clearable></el-input>
- </el-form-item>
- <el-form-item label="调用状态">
- <el-select v-model="callLogQuery.callStatus" placeholder="全部" clearable>
- <el-option label="成功" :value="0"></el-option>
- <el-option label="进行中" :value="1"></el-option>
- <el-option label="失败" :value="2"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="queryCallLogs">查询</el-button>
- <el-button @click="resetCallLogQuery">重置</el-button>
- </el-form-item>
- </el-form>
- <el-table :data="callLogList" border stripe v-loading="logLoading">
- <el-table-column prop="objCode" label="对象代码" min-width="150"></el-table-column>
- <el-table-column prop="objName" label="对象名称" min-width="200"></el-table-column>
- <el-table-column prop="abilityName" label="能力名称" min-width="150"></el-table-column>
- <el-table-column prop="callTime" label="调用时间" width="180"></el-table-column>
- <el-table-column label="调用状态" width="100" align="center">
- <template slot-scope="scope">
- <el-tag size="small" :type="getCallStatusType(scope.row.callStatus)">
- {{ formatCallStatus(scope.row.callStatus) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="80" align="center">
- <template slot-scope="scope">
- <el-button type="text" size="mini" @click="handleCallLogDetail(scope.row)">详情</el-button>
- </template>
- </el-table-column>
- </el-table>
- <pagination
- v-show="callLogTotal > 0"
- :total="callLogTotal"
- :page.sync="callLogQuery.pageNum"
- :limit.sync="callLogQuery.pageSize"
- @pagination="queryCallLogs"
- />
- </div>
- </el-tab-pane>
- <!-- 事件日志标签页 -->
- <el-tab-pane label="事件日志" name="eventLogs">
- <div class="tab-content">
- <el-form :inline="true" :model="eventLogQuery" class="log-filter">
- <el-form-item label="时间范围">
- <el-date-picker
- v-model="eventLogQuery.dateRange"
- type="datetimerange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="yyyy-MM-dd HH:mm:ss">
- </el-date-picker>
- </el-form-item>
- <el-form-item label="事件标识">
- <el-input v-model="eventLogQuery.eventKey" placeholder="请输入事件标识" clearable></el-input>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="queryEventLogs">查询</el-button>
- <el-button @click="resetEventLogQuery">重置</el-button>
- </el-form-item>
- </el-form>
- <el-table :data="eventLogList" border stripe v-loading="eventLogLoading">
- <el-table-column prop="objCode" label="对象编号" min-width="150"></el-table-column>
- <el-table-column prop="objName" label="对象名称" min-width="200"></el-table-column>
- <el-table-column prop="eventName" label="事件名称" min-width="200"></el-table-column>
- <el-table-column prop="eventTime" label="事件时间" width="180"></el-table-column>
- <el-table-column label="操作" width="80" align="center">
- <template slot-scope="scope">
- <el-button type="text" size="mini" @click="handleEventLogDetail(scope.row)">详情</el-button>
- </template>
- </el-table-column>
- </el-table>
- <pagination
- v-show="eventLogTotal > 0"
- :total="eventLogTotal"
- :page.sync="eventLogQuery.pageNum"
- :limit.sync="eventLogQuery.pageSize"
- @pagination="queryEventLogs"
- />
- </div>
- </el-tab-pane>
- </el-tabs>
- </el-card>
- <!-- 执行结果对话框 -->
- <el-dialog
- :title="executeDialog.title"
- :visible.sync="executeDialog.visible"
- width="600px">
- <div class="execute-result">
- <el-alert
- :title="executeDialog.status"
- :type="executeDialog.type"
- :description="executeDialog.message"
- show-icon>
- </el-alert>
- <div v-if="executeDialog.data" class="result-data">
- <pre>{{ JSON.stringify(executeDialog.data, null, 2) }}</pre>
- </div>
- </div>
- <span slot="footer">
- <el-button @click="executeDialog.visible = false">关闭</el-button>
- </span>
- </el-dialog>
- <!-- 调用日志详情弹窗 -->
- <el-dialog :visible.sync="callLogDetailDialog" title="调用日志详情" width="60%">
- <div v-if="callLogDetailData">
- <el-descriptions :column="2" border>
- <el-descriptions-item label="对象代码">{{ callLogDetailData.objCode }}</el-descriptions-item>
- <el-descriptions-item label="模型代码">{{ callLogDetailData.modelCode }}</el-descriptions-item>
- <el-descriptions-item label="能力标识">{{ callLogDetailData.abilityKey }}</el-descriptions-item>
- <el-descriptions-item label="调用时间">{{ callLogDetailData.callTime }}</el-descriptions-item>
- <el-descriptions-item label="响应时间">{{ callLogDetailData.resTime }}</el-descriptions-item>
- <el-descriptions-item label="调用结果">
- <el-tag :type="getCallStatusType(callLogDetailData.callStatus)">
- {{ formatCallStatus(callLogDetailData.callStatus) }}
- </el-tag>
- </el-descriptions-item>
- </el-descriptions>
- <div style="margin-top: 20px;">
- <h4>调用载体</h4>
- <pre style="white-space: pre-wrap; background-color: #f5f5f5; padding: 10px; border-radius: 4px; max-height: 300px; overflow: auto;">{{ callLogDetailData.callPayload }}</pre>
- </div>
- <div style="margin-top: 20px;">
- <h4>响应载体</h4>
- <pre style="white-space: pre-wrap; background-color: #f5f5f5; padding: 10px; border-radius: 4px; max-height: 300px; overflow: auto;">{{ callLogDetailData.resPayload }}</pre>
- </div>
- </div>
- </el-dialog>
- <!-- 事件日志详情弹窗 -->
- <el-dialog :visible.sync="eventLogDetailDialog" title="事件日志详情" width="60%">
- <div v-if="eventLogDetailData">
- <el-descriptions :column="2" border>
- <el-descriptions-item label="对象编号">{{ eventLogDetailData.objCode }}</el-descriptions-item>
- <el-descriptions-item label="对象名称">{{ eventLogDetailData.objName }}</el-descriptions-item>
- <el-descriptions-item label="事件名称">{{ eventLogDetailData.eventName }}</el-descriptions-item>
- <el-descriptions-item label="事件标识">{{ eventLogDetailData.eventKey }}</el-descriptions-item>
- <el-descriptions-item label="事件时间" :span="2">{{ eventLogDetailData.eventTime }}</el-descriptions-item>
- </el-descriptions>
- <div style="margin-top: 20px;">
- <h4>事件描述</h4>
- <div style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">
- {{ eventLogDetailData.eventDetail || '无' }}
- </div>
- </div>
- </div>
- </el-dialog>
- <!-- 能力执行弹窗(支持Slider和Input类型) -->
- <el-dialog :title="abilityDialogTitle" :visible.sync="abilityDialogVisible" width="500px" append-to-body :close-on-click-modal="false">
- <el-form ref="abilityForm" :model="abilityForm" label-width="100px">
- <el-form-item label="能力名称">
- <el-input v-model="abilityForm.abilityName" disabled></el-input>
- </el-form-item>
- <el-form-item label="能力描述">
- <el-input type="textarea" v-model="abilityForm.abilityDesc" disabled :rows="2"></el-input>
- </el-form-item>
- <!-- 根据参数类型显示不同的输入组件 -->
- <el-form-item label="参数设置" v-if="abilityParamType">
- <!-- Slider类型:显示滑块 -->
- <template v-if="abilityParamType === 'Slider'">
- <div style="width: 100%;">
- <div style="padding: 0 10px;">
- <el-slider
- v-model="abilitySliderValue"
- :min="abilitySliderMin"
- :max="abilitySliderMax"
- :show-tooltip="true"
- :format-tooltip="formatTooltip">
- </el-slider>
- </div>
- <div style="display: flex; justify-content: space-between; padding: 0 10px; margin-top: -5px; margin-bottom: 20px;">
- <span style="font-size: 12px; color: #909399;">{{ abilitySliderMin }}%</span>
- <span style="font-size: 12px; color: #909399;">{{ Math.floor((abilitySliderMin + abilitySliderMax) / 2) }}%</span>
- <span style="font-size: 12px; color: #909399;">{{ abilitySliderMax }}%</span>
- </div>
- <div style="display: flex; align-items: center; gap: 15px;">
- <el-input-number
- v-model="abilitySliderValue"
- :min="abilitySliderMin"
- :max="abilitySliderMax"
- :step="1"
- :precision="0"
- controls-position="right"
- style="flex: 0 0 120px;">
- </el-input-number>
- <span style="color: #909399; font-size: 13px;">
- 调节范围:{{ abilitySliderMin }} - {{ abilitySliderMax }}
- </span>
- </div>
- </div>
- </template>
- <!-- Input类型:显示输入框 -->
- <template v-else-if="abilityParamType === 'Input'">
- <el-input
- v-model="abilityInputValue"
- placeholder="请输入参数值"
- clearable>
- </el-input>
- </template>
- </el-form-item>
- </el-form>
- <div slot="footer" class="dialog-footer">
- <el-button @click="abilityDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="executeAbilityWithParam" :loading="abilityExecuting">执行</el-button>
- </div>
- </el-dialog>
- <!-- Options类型的快速执行弹窗 -->
- <el-dialog :title="quickAbilityTitle" :visible.sync="quickAbilityDialogVisible" width="400px" append-to-body>
- <div style="text-align: center; padding: 20px 0;">
- <el-button-group>
- <el-button
- v-for="option in quickAbilityOptions"
- :key="option.value"
- type="primary"
- size="medium"
- @click="executeQuickAbility(option.value)"
- :loading="quickAbilityExecuting && quickAbilityValue === option.value"
- style="margin: 0 10px;">
- {{ option.key }}
- </el-button>
- </el-button-group>
- </div>
- </el-dialog>
- <!-- 编辑系统信息对话框 -->
- <el-dialog title="编辑系统信息" :visible.sync="editSystemDialogVisible" width="600px" append-to-body :close-on-click-modal="false">
- <el-form ref="editSystemForm" :model="editSystemForm" :rules="editSystemRules" label-width="100px">
- <el-form-item label="系统代码" prop="systemCode">
- <el-input v-model="editSystemForm.systemCode" placeholder="请输入系统代码" disabled />
- </el-form-item>
- <el-form-item label="系统名称" prop="systemName">
- <el-input v-model="editSystemForm.systemName" placeholder="请输入系统名称" />
- </el-form-item>
- <el-form-item label="系统简称" prop="shortName">
- <el-input v-model="editSystemForm.shortName" placeholder="请输入系统简称" />
- </el-form-item>
- <el-form-item label="对接厂商" prop="manFacturer">
- <el-input v-model="editSystemForm.manFacturer" placeholder="请输入对接厂商" />
- </el-form-item>
- <el-form-item label="联系人" prop="contactPerson">
- <el-input v-model="editSystemForm.contactPerson" placeholder="请输入联系人" />
- </el-form-item>
- <el-form-item label="联系电话" prop="contactNumber">
- <el-input v-model="editSystemForm.contactNumber" placeholder="请输入联系电话" />
- </el-form-item>
- <el-form-item label="维护人" prop="maintainerPerson">
- <el-input v-model="editSystemForm.maintainerPerson" placeholder="请输入维护人" />
- </el-form-item>
- <el-form-item label="维护电话" prop="maintainerNumber">
- <el-input v-model="editSystemForm.maintainerNumber" placeholder="请输入维护电话" />
- </el-form-item>
- <el-form-item label="备注说明" prop="descr">
- <el-input v-model="editSystemForm.descr" type="textarea" :rows="3" placeholder="请输入备注说明" />
- </el-form-item>
- </el-form>
- <div slot="footer" class="dialog-footer">
- <el-button @click="editSystemDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitEditSystem" :loading="editSystemSubmitting">保存</el-button>
- </div>
- </el-dialog>
- </div>
- </template>
- <script>
- import { getSubsystemByCode, updateSubsystem } from '@/api/adapter/subsystem'
- import { getModelByCode } from '@/api/basecfg/objModel'
- import { getObjAttr, getObjAttrBatch } from '@/api/basecfg/objAttribute'
- import { callAbility } from '@/api/basecfg/objAbility'
- import { getByCondition } from '@/api/device/device'
- import { listCallLog, listEventLog, getCallLog, getEventLog } from '@/api/basecfg/objLog'
- export default {
- name: 'SmartLightingSystem',
- data() {
- return {
- // 系统代码
- systemCode: 'SYS_ZHZM',
- // 系统信息
- systemInfo: {},
- // 系统状态
- systemStatus: '1',
- // 当前标签页
- activeTab: 'overview',
- // 协议属性
- protocolAttrs: [],
- // 状态属性
- stateAttrs: [],
- // 基础属性
- baseAttrs: [],
- // 项目列表
- projectList: [],
- // 分组列表
- subsetList: [],
- // 系统能力
- systemAbilities: [],
- abilityLoading: false,
- // 智慧灯杆列表
- lampPoleList: [],
- lampPoleLoading: false,
- lampPoleAttrs: {},
- // 密码可见状态
- passwordVisible: false,
- // 灯杆物模型
- lampPoleModel: null,
- // 灯杆能力列表
- lampPoleAbilities: {},
- // 当前操作的灯杆能力列表
- currentLampPoleAbilities: [],
- // 灯组设备列表(所有灯组)
- lampGroupList: [],
- lampGroupAttrs: {},
- lampGroupModel: null,
- lampGroupAbilities: {},
- // 当前操作的灯组能力列表
- currentLampGroupAbilities: [],
- // 灯组统计
- totalLampCount: 0,
- onlineLampCount: 0,
- // 集中器设备列表
- concentratorList: [],
- concentratorLoading: false,
- concentratorAttrs: {},
- // 设备统计
- deviceStats: {
- total: 0,
- online: 0,
- offline: 0,
- lamps: 0,
- concentrators: 0
- },
- // 灯组统计
- lampStats: {
- on: 0,
- off: 0
- },
- // 总功率
- totalPower: 0,
- // 快速操作
- quickActions: [
- { key: 'syncProject', name: '同步项目', icon: 'el-icon-refresh', loading: false },
- { key: 'syncDevice', name: '同步设备', icon: 'el-icon-download', loading: false },
- { key: 'allOn', name: '全部开灯', icon: 'el-icon-sunny', loading: false },
- { key: 'allOff', name: '全部关灯', icon: 'el-icon-moon', loading: false }
- ],
- // 所有模型代码(系统 + 设备)
- allModelCodes: [],
- // 调用日志查询
- callLogQuery: {
- dateRange: [],
- abilityKey: '',
- callStatus: '',
- pageNum: 1,
- pageSize: 10
- },
- callLogList: [],
- callLogTotal: 0,
- logLoading: false,
- // 事件日志查询
- eventLogQuery: {
- dateRange: [],
- eventKey: '',
- pageNum: 1,
- pageSize: 10
- },
- eventLogList: [],
- eventLogTotal: 0,
- eventLogLoading: false,
- // 调用日志详情
- callLogDetailDialog: false,
- callLogDetailData: null,
- // 事件日志详情
- eventLogDetailDialog: false,
- eventLogDetailData: null,
- // 执行结果对话框
- executeDialog: {
- visible: false,
- title: '',
- status: '',
- type: 'success',
- message: '',
- data: null
- },
- // 能力执行相关
- abilityDialogVisible: false,
- abilityDialogTitle: '能力执行',
- abilityForm: {
- abilityName: '',
- abilityKey: '',
- abilityDesc: '',
- paramDefinition: null,
- modelCode: ''
- },
- abilityParamType: null,
- abilitySliderValue: 50,
- abilitySliderMin: 0,
- abilitySliderMax: 100,
- abilityInputValue: '',
- abilityExecuting: false,
- currentDevice: null,
- currentObjType: null, // 1:设施, 2:设备, 3:系统
- // 快速执行弹窗(Options类型)
- quickAbilityDialogVisible: false,
- quickAbilityTitle: '',
- quickAbilityOptions: [],
- quickAbilityExecuting: false,
- quickAbilityValue: null,
- quickAbilityData: null,
- // 编辑系统信息
- editSystemDialogVisible: false,
- editSystemSubmitting: false,
- editSystemForm: {
- id: null,
- systemCode: null,
- systemName: null,
- shortName: null,
- manFacturer: null,
- contactPerson: null,
- contactNumber: null,
- maintainerPerson: null,
- maintainerNumber: null,
- descr: null,
- modelCode: null
- },
- editSystemRules: {
- systemCode: [
- { required: true, message: '系统代码不能为空', trigger: 'blur' }
- ],
- systemName: [
- { required: true, message: '系统名称不能为空', trigger: 'blur' }
- ]
- }
- }
- },
- created() {
- this.loadSystemInfo()
- this.initDateRange()
- },
- methods: {
- // 格式化滑块tooltip
- formatTooltip(val) {
- return `${val}%`
- },
- // 解析参数定义类型
- getParamType(paramDefinition) {
- if (!paramDefinition) return null
- try {
- const def = JSON.parse(paramDefinition)
- return def.type
- } catch (e) {
- return null
- }
- },
- // 获取参数类型名称
- getParamTypeName(paramDefinition) {
- const type = this.getParamType(paramDefinition)
- const typeNames = {
- 'Options': '选项型',
- 'Slider': '滑块型',
- 'Input': '输入型'
- }
- return typeNames[type] || '无参数'
- },
- // 获取参数类型标签样式
- getParamTypeTagType(paramDefinition) {
- const type = this.getParamType(paramDefinition)
- const typeStyles = {
- 'Options': 'primary',
- 'Slider': 'success',
- 'Input': 'warning'
- }
- return typeStyles[type] || 'info'
- },
- // 解析Options类型的选项
- parseOptions(paramDefinition) {
- try {
- const def = JSON.parse(paramDefinition)
- if (def.type === 'Options' && def.list) {
- return def.list
- }
- } catch (e) {
- console.error('解析Options失败:', e)
- }
- return []
- },
- // 获取开关控制按钮的类型(根据当前状态)
- getLightControlButtonType(deviceCode, optionValue) {
- const currentState = this.getLampGroupAttrValue(deviceCode, 'Switch')
- // 关灯状态(0):开灯按钮绿色,关灯按钮灰色
- // 开灯状态(1):关灯按钮绿色,开灯按钮灰色
- if (currentState === '0') {
- // 当前关灯,开灯按钮高亮
- return optionValue === '1' ? 'success' : 'info'
- } else if (currentState === '1') {
- // 当前开灯,关灯按钮高亮
- return optionValue === '0' ? 'success' : 'info'
- }
- // 状态未知时,默认样式
- return 'primary'
- },
- // 加载灯组能力列表(用于下拉菜单)
- loadLampGroupAbilities(lampGroup) {
- // 从已初始化的 lampGroupAbilities 中获取能力列表
- const abilities = this.lampGroupAbilities[lampGroup.deviceCode]
- if (!abilities || abilities.length === 0) {
- this.currentLampGroupAbilities = []
- console.warn('未找到灯组能力列表:', lampGroup.deviceCode)
- return
- }
- // 过滤出 hiddenFlag 为 1 的能力(可在界面显示的)
- this.currentLampGroupAbilities = abilities.filter(ability => ability.hiddenFlag === 1)
- console.log('加载灯组能力:', lampGroup.deviceCode, this.currentLampGroupAbilities)
- },
- // 加载灯杆能力列表(用于下拉菜单)
- loadLampPoleAbilities(lampPole) {
- const abilities = this.lampPoleAbilities[lampPole.deviceCode]
- if (!abilities || abilities.length === 0) {
- this.currentLampPoleAbilities = []
- console.warn('未找到灯杆能力列表:', lampPole.deviceCode)
- return
- }
- this.currentLampPoleAbilities = abilities
- console.log('加载灯杆能力:', lampPole.deviceCode, this.currentLampPoleAbilities)
- },
- // 处理灯组能力命令(下拉菜单点击)
- handleLampGroupAbilityCommand(command) {
- const { ability, device } = command
- this.currentDevice = device
- const paramType = this.getParamType(ability.paramDefinition)
- if (paramType === 'Options') {
- // Options类型:显示快速执行弹窗
- this.quickAbilityTitle = ability.abilityName
- this.quickAbilityOptions = this.parseOptions(ability.paramDefinition)
- this.quickAbilityData = ability
- this.quickAbilityDialogVisible = true
- } else if (paramType === 'Slider') {
- // Slider类型:显示滑块弹窗
- this.showSliderDialog(ability, device, 2)
- } else if (paramType === 'Input') {
- // Input类型:显示输入弹窗
- this.showInputDialog(ability, device, 2)
- } else {
- // 无参数:直接执行
- this.executeDirectLampGroupAbility(ability, device)
- }
- },
- // 处理灯杆能力命令(下拉菜单点击)
- handleLampPoleAbilityCommand(command) {
- const { ability, device } = command
- this.currentDevice = device
- const paramType = this.getParamType(ability.paramDefinition)
- if (paramType === 'Options') {
- // Options类型:显示快速执行弹窗
- this.quickAbilityTitle = `${ability.abilityName} - ${device.deviceName || device.deviceCode}`
- this.quickAbilityOptions = this.parseOptions(ability.paramDefinition)
- this.quickAbilityData = ability
- this.quickAbilityDialogVisible = true
- } else if (paramType === 'Slider') {
- // Slider类型:显示滑块弹窗
- this.showSliderDialog(ability, device, 2)
- } else if (paramType === 'Input') {
- // Input类型:显示输入弹窗
- this.showInputDialog(ability, device, 2)
- } else {
- // 无参数:直接执行
- this.executeDirectLampPoleAbility(ability, device)
- }
- },
- // 快速执行Options类型能力(通用方法)
- async executeQuickAbility(value) {
- if (!this.currentDevice || !this.quickAbilityData) {
- this.$message.error('操作数据异常')
- return
- }
- this.quickAbilityExecuting = true
- this.quickAbilityValue = value
- try {
- // 判断是灯杆还是灯组
- const isLampPole = this.currentDevice.deviceModel === 'M_Z010_DEV_SQUARE_LAMP_POST'
- const modelCode = this.currentDevice.deviceModel || (isLampPole ? 'M_Z010_DEV_SQUARE_LAMP_POST' : 'M_Z010_DEV_SQUARE_LIGHT')
- await callAbility({
- objCode: this.currentDevice.deviceCode,
- objType: 2,
- modelCode: modelCode,
- abilityKey: this.quickAbilityData.abilityKey,
- abilityParam: value
- })
- this.$message.success(`${this.quickAbilityData.abilityName}执行成功`)
- this.quickAbilityDialogVisible = false
- // 根据设备类型刷新不同的数据
- if (isLampPole) {
- // 灯杆:刷新该灯杆下所有灯组的属性
- await this.refreshLampPoleDevices(this.currentDevice.deviceCode)
- } else {
- // 灯组:刷新单个灯组属性
- await this.loadSingleLampGroupAttr(this.currentDevice.deviceCode, modelCode)
- }
- } catch (error) {
- this.$message.error(`${this.quickAbilityData.abilityName}执行失败:${error.message}`)
- } finally {
- this.quickAbilityExecuting = false
- this.quickAbilityValue = null
- }
- },
- // 直接执行灯组能力(无参数,从下拉菜单)
- async executeDirectLampGroupAbility(ability, device) {
- this.$confirm(`确认执行 ${ability.abilityName} 操作?`, '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(async () => {
- try {
- const modelCode = device.deviceModel || 'M_Z010_DEV_SQUARE_LIGHT'
- await callAbility({
- objCode: device.deviceCode,
- objType: 2,
- modelCode: modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: null
- })
- this.$message.success(`${ability.abilityName}执行成功`)
- await this.loadSingleLampGroupAttr(device.deviceCode, modelCode)
- } catch (error) {
- this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
- }
- })
- },
- // 直接执行灯杆能力(无参数,从下拉菜单)
- async executeDirectLampPoleAbility(ability, device) {
- this.$confirm(`确认执行 ${ability.abilityName} 操作?`, '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(async () => {
- try {
- const modelCode = device.deviceModel || 'M_Z010_DEV_SQUARE_LAMP_POST'
- await callAbility({
- objCode: device.deviceCode,
- objType: 2,
- modelCode: modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: null
- })
- this.$message.success(`${ability.abilityName}执行成功`)
- // 刷新灯杆下所有灯组的属性
- await this.refreshLampPoleDevices(device.deviceCode)
- } catch (error) {
- this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
- }
- })
- },
- // 处理系统能力点击
- handleSystemAbilityClick(ability) {
- const paramType = this.getParamType(ability.paramDefinition)
- if (paramType === 'Slider') {
- this.showSliderDialog(ability, this.systemInfo, 3)
- } else if (paramType === 'Input') {
- this.showInputDialog(ability, this.systemInfo, 3)
- } else {
- // 无参数:直接执行
- this.executeSystemAbility(ability)
- }
- },
- // 执行系统能力(带Options参数)
- executeSystemAbilityWithParam(ability, paramValue) {
- ability.executing = true
- ability.executingValue = paramValue
- callAbility({
- objCode: this.systemCode,
- objType: 3,
- modelCode: this.systemInfo.modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: paramValue
- }).then(res => {
- this.executeDialog = {
- visible: true,
- title: '执行结果 - ' + ability.abilityName,
- status: '执行成功',
- type: 'success',
- message: '系统能力执行完成',
- data: res.data
- }
- }).catch(error => {
- this.executeDialog = {
- visible: true,
- title: '执行结果 - ' + ability.abilityName,
- status: '执行失败',
- type: 'error',
- message: error.message,
- data: null
- }
- }).finally(() => {
- ability.executing = false
- ability.executingValue = null
- })
- },
- // 执行系统能力(无参数)
- executeSystemAbility(ability) {
- ability.executing = true
- callAbility({
- objCode: this.systemCode,
- objType: 3,
- modelCode: this.systemInfo.modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: null
- }).then(res => {
- this.executeDialog = {
- visible: true,
- title: '执行结果 - ' + ability.abilityName,
- status: '执行成功',
- type: 'success',
- message: '系统能力执行完成',
- data: res.data
- }
- // 刷新相关数据
- if (ability.abilityKey === 'GetProjectList' || ability.abilityKey === 'GetProjectSubsetList') {
- this.loadSystemInfo()
- } else if (ability.abilityKey === 'GetDeviceList') {
- this.loadLampPoleList()
- this.loadConcentratorDevices()
- }
- }).catch(error => {
- this.executeDialog = {
- visible: true,
- title: '执行结果 - ' + ability.abilityName,
- status: '执行失败',
- type: 'error',
- message: error.message,
- data: null
- }
- }).finally(() => {
- ability.executing = false
- })
- },
- // 显示滑块弹窗
- showSliderDialog(ability, obj, objType) {
- this.currentDevice = obj
- this.currentObjType = objType
- this.abilityForm = { ...ability }
- // 根据对象类型设置标题
- let objName
- if (objType === 3) {
- objName = this.systemInfo.systemName
- } else if (objType === 2) {
- objName = obj.deviceName || obj.deviceCode
- }
- this.abilityDialogTitle = `${ability.abilityName} - ${objName}`
- this.abilityParamType = 'Slider'
- try {
- const def = JSON.parse(ability.paramDefinition)
- this.abilitySliderMin = def.min || 0
- this.abilitySliderMax = def.max || 100
- this.abilitySliderValue = Math.floor((this.abilitySliderMin + this.abilitySliderMax) / 2)
- } catch (e) {
- console.error('解析Slider参数失败:', e)
- this.abilitySliderMin = 0
- this.abilitySliderMax = 100
- this.abilitySliderValue = 50
- }
- this.abilityDialogVisible = true
- },
- // 显示输入弹窗
- showInputDialog(ability, obj, objType) {
- this.currentDevice = obj
- this.currentObjType = objType
- this.abilityForm = { ...ability }
- // 根据对象类型设置标题
- let objName
- if (objType === 3) {
- objName = this.systemInfo.systemName
- } else if (objType === 2) {
- objName = obj.deviceName || obj.deviceCode
- }
- this.abilityDialogTitle = `${ability.abilityName} - ${objName}`
- this.abilityParamType = 'Input'
- this.abilityInputValue = ''
- this.abilityDialogVisible = true
- },
- // 执行带参数的能力
- async executeAbilityWithParam() {
- let paramValue = null
- if (this.abilityParamType === 'Slider') {
- paramValue = String(this.abilitySliderValue)
- } else if (this.abilityParamType === 'Input') {
- if (!this.abilityInputValue) {
- this.$message.warning('请输入参数值')
- return
- }
- paramValue = this.abilityInputValue
- }
- this.abilityExecuting = true
- // 构建调用参数
- const callParams = {
- objType: this.currentObjType,
- abilityKey: this.abilityForm.abilityKey,
- abilityParam: paramValue
- }
- // 根据对象类型设置不同的参数
- if (this.currentObjType === 3) {
- // 系统
- callParams.objCode = this.systemCode
- callParams.modelCode = this.systemInfo.modelCode
- } else if (this.currentObjType === 2) {
- // 设备(包括灯组和灯杆)
- callParams.objCode = this.currentDevice.deviceCode
- const isLampPole = this.currentDevice.deviceModel === 'M_Z010_DEV_SQUARE_LAMP_POST'
- callParams.modelCode = this.currentDevice.deviceModel || (isLampPole ? 'M_Z010_DEV_SQUARE_LAMP_POST' : 'M_Z010_DEV_SQUARE_LIGHT')
- }
- try {
- await callAbility(callParams)
- this.$message.success('执行成功')
- this.abilityDialogVisible = false
- // 根据对象类型刷新数据
- if (this.currentObjType === 3) {
- // 系统能力执行后的处理
- if (this.abilityForm.abilityKey === 'GetProjectList' ||
- this.abilityForm.abilityKey === 'GetProjectSubsetList') {
- await this.loadSystemInfo()
- } else if (this.abilityForm.abilityKey === 'GetDeviceList') {
- await this.loadLampPoleList()
- await this.loadConcentratorDevices()
- }
- } else if (this.currentObjType === 2) {
- // 设备能力执行后,根据设备类型刷新
- const isLampPole = this.currentDevice.deviceModel === 'M_Z010_DEV_SQUARE_LAMP_POST'
- if (isLampPole) {
- // 灯杆:刷新该灯杆下所有灯组
- await this.refreshLampPoleDevices(this.currentDevice.deviceCode)
- } else {
- // 灯组:重新加载单个设备属性,传入modelCode
- const modelCode = this.currentDevice.deviceModel || 'M_Z010_DEV_SQUARE_LIGHT'
- await this.loadSingleLampGroupAttr(this.currentDevice.deviceCode, modelCode)
- }
- }
- } catch (error) {
- this.$message.error('执行失败:' + error.message)
- } finally {
- this.abilityExecuting = false
- }
- },
- // 初始化时间范围(默认今天)
- initDateRange() {
- const now = new Date()
- const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)
- const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
- this.callLogQuery.dateRange = [
- this.formatDate(startOfDay),
- this.formatDate(endOfDay)
- ]
- this.eventLogQuery.dateRange = [
- this.formatDate(startOfDay),
- this.formatDate(endOfDay)
- ]
- },
- // 格式化日期
- formatDate(date) {
- if (!date) return ''
- const year = date.getFullYear()
- const month = (date.getMonth() + 1).toString().padStart(2, '0')
- const day = date.getDate().toString().padStart(2, '0')
- const hours = date.getHours().toString().padStart(2, '0')
- const minutes = date.getMinutes().toString().padStart(2, '0')
- const seconds = date.getSeconds().toString().padStart(2, '0')
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
- },
- // 加载系统信息
- async loadSystemInfo() {
- try {
- // 1. 获取系统基础信息
- const sysRes = await getSubsystemByCode(this.systemCode)
- this.systemInfo = sysRes.data
- // 2. 获取模型信息
- if (this.systemInfo.modelCode) {
- this.allModelCodes.push(this.systemInfo.modelCode)
- const modelRes = await getModelByCode(this.systemInfo.modelCode)
- const modelData = modelRes.data
- // 获取能力列表,添加执行状态
- this.systemAbilities = (modelData.abilityList || []).map(item => ({
- ...item,
- executing: false,
- executingValue: null
- }))
- }
- // 3. 获取系统属性值(系统不需要 modelCode)
- const attrRes = await getObjAttr(3, this.systemCode)
- const attrData = attrRes.data
- this.protocolAttrs = attrData.Protocol || []
- this.stateAttrs = attrData.State || []
- this.baseAttrs = attrData.Base || []
- // 解析项目列表和分组列表
- this.parseBaseAttributes()
- // 设置系统状态
- const statusAttr = this.stateAttrs.find(attr => attr.attrKey === 'interfaceStatus')
- if (statusAttr) {
- this.systemStatus = statusAttr.attrValue
- }
- // 4. 加载智慧灯杆列表
- await this.loadLampPoleList()
- // 5. 加载集中器设备
- await this.loadConcentratorDevices()
- } catch (error) {
- this.$message.error('加载系统信息失败:' + error.message)
- console.error('加载系统信息错误:', error)
- }
- },
- async loadDeviceAttrsBatch(modelCode, targetAttrsObj) {
- if (!modelCode) {
- console.warn('modelCode为空,跳过批量加载属性')
- return
- }
- try {
- const res = await getObjAttrBatch(2, modelCode)
- // res.data 是一个对象,key是deviceCode,value是原来单个接口的data部分
- if (res.data) {
- Object.keys(res.data).forEach(deviceCode => {
- this.$set(targetAttrsObj, deviceCode, res.data[deviceCode])
- })
- }
- } catch (error) {
- console.error(`批量加载设备属性失败(${modelCode}):`, error)
- }
- },
- // 解析基础属性中的项目和分组信息
- parseBaseAttributes() {
- this.baseAttrs.forEach(attr => {
- if (attr.attrKey === 'projectList' && attr.attrValue) {
- try {
- this.projectList = JSON.parse(attr.attrValue)
- } catch (e) {
- console.error('解析项目列表失败', e)
- }
- }
- if (attr.attrKey === 'projectSubsetList' && attr.attrValue) {
- try {
- this.subsetList = JSON.parse(attr.attrValue)
- } catch (e) {
- console.error('解析分组列表失败', e)
- }
- }
- })
- },
- // 加载智慧灯杆列表
- async loadLampPoleList() {
- this.lampPoleLoading = true
- try {
- // 1. 查询灯杆设备列表
- const res = await getByCondition({
- subsystemCode: this.systemCode,
- deviceModel: 'M_Z010_DEV_SQUARE_LAMP_POST'
- })
- const poleData = res.data || res.rows || []
- this.lampPoleList = poleData.map(pole => ({
- ...pole,
- detailTab: 'lamps'
- }))
- if (this.lampPoleList.length === 0) {
- return
- }
- // 2. 加载灯杆物模型(用于获取能力列表)
- await this.loadLampPoleModel()
- // 3. 批量加载灯杆属性(获取subDev)
- await this.loadDeviceAttrsBatch('M_Z010_DEV_SQUARE_LAMP_POST', this.lampPoleAttrs)
- // 4. 收集所有灯组的deviceCode
- const allLampGroupCodes = []
- this.lampPoleList.forEach(pole => {
- const lampGroups = this.getLampGroupCodesFromPole(pole.deviceCode)
- allLampGroupCodes.push(...lampGroups)
- })
- // 5. 加载灯组物模型定义
- await this.loadLampGroupModel()
- // 6. 批量加载所有灯组的属性
- if (allLampGroupCodes.length > 0) {
- await this.loadDeviceAttrsBatch('M_Z010_DEV_SQUARE_LIGHT', this.lampGroupAttrs)
- }
- // 7. 构建灯组设备列表(用于统计和显示)
- this.lampGroupList = allLampGroupCodes.map(item => ({
- deviceCode: item.deviceCode,
- deviceModel: item.modelCode,
- detailTab: 'attrs'
- }))
- // 8. 统计数据
- this.calculateLampGroupStats()
- this.updateDeviceStats()
- // 9. 为所有灯组初始化能力列表
- this.initAllLampGroupAbilities()
- // 10. 为所有灯杆初始化能力列表
- this.initAllLampPoleAbilities()
- } catch (error) {
- this.$message.error('加载灯杆列表失败')
- console.error('加载灯杆列表错误:', error)
- } finally {
- this.lampPoleLoading = false
- }
- },
- // 为所有灯组初始化能力列表
- initAllLampGroupAbilities() {
- if (!this.lampGroupModel || !this.lampGroupModel.abilityList) {
- console.warn('灯组模型或能力列表为空')
- return
- }
- this.lampGroupList.forEach(lamp => {
- const abilities = this.lampGroupModel.abilityList.map(ability => ({
- ...ability,
- executing: false,
- executingValue: null
- }))
- this.$set(this.lampGroupAbilities, lamp.deviceCode, abilities)
- })
- console.log('已为', this.lampGroupList.length, '个灯组初始化能力列表')
- },
- // 为所有灯杆初始化能力列表
- initAllLampPoleAbilities() {
- if (!this.lampPoleModel || !this.lampPoleModel.abilityList) {
- console.warn('灯杆模型或能力列表为空')
- return
- }
- this.lampPoleList.forEach(pole => {
- const abilities = this.lampPoleModel.abilityList
- .filter(ability => ability.hiddenFlag === 1) // 只保留可显示的能力
- .map(ability => ({
- ...ability,
- executing: false,
- executingValue: null
- }))
- this.$set(this.lampPoleAbilities, pole.deviceCode, abilities)
- })
- console.log('已为', this.lampPoleList.length, '个灯杆初始化能力列表')
- },
- // 从灯杆属性中获取灯组代码列表(包含modelCode)
- getLampGroupCodesFromPole(poleCode) {
- const attrs = this.lampPoleAttrs[poleCode]
- if (!attrs || !attrs.Base) return []
- const subDevAttr = attrs.Base.find(a => a.attrKey === 'subDev')
- if (subDevAttr && subDevAttr.attrValue) {
- try {
- const subDevData = JSON.parse(subDevAttr.attrValue)
- // 新格式:subDev 是对象数组 [{deviceCode, modelCode}, ...]
- if (Array.isArray(subDevData)) {
- return subDevData.map(item => {
- // 兼容新旧格式
- if (typeof item === 'string') {
- return { deviceCode: item, modelCode: 'M_Z010_DEV_SQUARE_LIGHT' }
- }
- return { deviceCode: item.deviceCode, modelCode: item.modelCode }
- })
- }
- return []
- } catch (e) {
- console.error('解析灯组列表失败', e)
- return []
- }
- }
- return []
- },
- // 加载灯组物模型定义
- async loadLampGroupModel() {
- try {
- const res = await getModelByCode('M_Z010_DEV_SQUARE_LIGHT')
- this.lampGroupModel = res.data
- } catch (error) {
- console.error('加载灯组模型失败:', error)
- }
- },
- // 加载灯杆物模型定义
- async loadLampPoleModel() {
- try {
- const res = await getModelByCode('M_Z010_DEV_SQUARE_LAMP_POST')
- this.lampPoleModel = res.data
- console.log('灯杆物模型加载成功:', this.lampPoleModel)
- } catch (error) {
- console.error('加载灯杆模型失败:', error)
- }
- },
- // 根据灯杆代码获取灯组列表
- getLampGroupList(poleCode) {
- const lampGroups = this.getLampGroupCodesFromPole(poleCode)
- return lampGroups.map(item => ({
- deviceCode: item.deviceCode,
- deviceModel: item.modelCode,
- detailTab: 'attrs'
- }))
- },
- // 获取灯组模型属性(按属性组)
- getLampGroupModelAttrs(deviceCode, attrGroup) {
- if (!this.lampGroupModel || !this.lampGroupModel.attrList) return []
- return this.lampGroupModel.attrList.filter(attr => attr.attrGroup === attrGroup)
- },
- // 获取灯组属性值
- getLampGroupAttrValue(deviceCode, attrKey) {
- const attrs = this.lampGroupAttrs[deviceCode]
- if (!attrs) return null
- // 搜索所有属性组
- for (const group in attrs) {
- if (attrs[group] && Array.isArray(attrs[group])) {
- const attr = attrs[group].find(a => a.attrKey === attrKey)
- if (attr) {
- return attr.attrValue
- }
- }
- }
- return null
- },
- // 获取灯组枚举属性名称
- getLampGroupAttrEnumName(deviceCode, attrKey, valueEnums) {
- const value = this.getLampGroupAttrValue(deviceCode, attrKey)
- if (!value || !valueEnums) return value || '-'
- const enumItem = valueEnums.find(e => e.attrValue === value)
- return enumItem ? enumItem.attrValueName : value
- },
- // 获取灯组更新时间
- getLampGroupUpdateTime(deviceCode) {
- const attrs = this.lampGroupAttrs[deviceCode]
- if (!attrs) return null
- // 从State组获取第一个有更新时间的属性
- if (attrs.State && attrs.State.length > 0) {
- const attrWithTime = attrs.State.find(a => a.updateTime)
- return attrWithTime ? attrWithTime.updateTime : null
- }
- return null
- },
- // 获取灯杆下的在线灯组数量
- getOnlineLampGroupCount(poleCode) {
- const lampGroups = this.getLampGroupList(poleCode)
- return lampGroups.filter(lamp => {
- const deviceStatus = this.getLampGroupAttrValue(lamp.deviceCode, 'deviceStatus')
- return deviceStatus === '1'
- }).length
- },
- // 获取灯杆下的在线灯组数量
- getOnSwitchLampGroupCount(poleCode) {
- const lampGroups = this.getLampGroupList(poleCode)
- return lampGroups.filter(lamp => {
- const deviceStatus = this.getLampGroupAttrValue(lamp.deviceCode, 'Switch')
- return deviceStatus === '1'
- }).length
- },
- // 统计灯组数据
- calculateLampGroupStats() {
- this.totalLampCount = this.lampGroupList.length
- this.onlineLampCount = this.lampGroupList.filter(lamp => {
- const deviceStatus = this.getLampGroupAttrValue(lamp.deviceCode, 'deviceStatus')
- return deviceStatus === '1'
- }).length
- // 统计开启的灯组和总功率
- let onCount = 0
- let totalPower = 0
- this.lampGroupList.forEach(lamp => {
- const lampOnOff = this.getLampGroupAttrValue(lamp.deviceCode, 'Switch')
- if (lampOnOff === '1') {
- onCount++
- const power = parseFloat(this.getLampGroupAttrValue(lamp.deviceCode, 'power')) || 0
- totalPower += power
- }
- })
- this.lampStats.on = onCount
- this.lampStats.off = this.totalLampCount - onCount
- this.totalPower = totalPower
- },
- // 处理灯杆行点击
- handleLampPoleClick(row, column, event) {
- if (column && column.type === 'expand') {
- return
- }
- this.$refs.lampPoleTable?.toggleRowExpansion(row)
- },
- // 处理灯组行点击
- handleLampGroupClick(row, column, event, poleIndex) {
- if (column && column.type === 'expand') {
- return
- }
- const tableRef = this.$refs[`lampGroupTable_${poleIndex}`]
- if (tableRef && tableRef[0]) {
- tableRef[0].toggleRowExpansion(row)
- }
- },
- // 处理灯组能力点击(在展开面板中)
- handleLampGroupAbilityClick(lampGroup, ability) {
- const paramType = this.getParamType(ability.paramDefinition)
- if (paramType === 'Slider') {
- this.showSliderDialog(ability, lampGroup, 2)
- } else if (paramType === 'Input') {
- this.showInputDialog(ability, lampGroup, 2)
- } else {
- // 无参数:直接执行
- this.executeLampGroupAbility(lampGroup, ability)
- }
- },
- // 执行灯组能力(无参数)- 在展开面板中调用
- async executeLampGroupAbility(lampGroup, ability) {
- ability.executing = true
- try {
- // 确保获取正确的modelCode
- const modelCode = lampGroup.deviceModel || 'M_Z010_DEV_SQUARE_LIGHT'
- console.log('执行灯组能力(无参数):', {
- deviceCode: lampGroup.deviceCode,
- modelCode: modelCode,
- abilityKey: ability.abilityKey
- })
- await callAbility({
- objCode: lampGroup.deviceCode,
- objType: 2,
- modelCode: modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: null
- })
- this.$message.success(`${ability.abilityName}执行成功`)
- // 重新加载单个灯组属性,必须传入modelCode
- await this.loadSingleLampGroupAttr(lampGroup.deviceCode, modelCode)
- } catch (error) {
- console.error('执行灯组能力失败:', error)
- this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
- } finally {
- ability.executing = false
- }
- },
- // 执行灯组能力(带参数)- 在展开面板中调用
- async executeLampGroupAbilityWithParam(lampGroup, ability, paramValue) {
- ability.executing = true
- ability.executingValue = paramValue
- try {
- // 确保获取正确的modelCode
- const modelCode = lampGroup.deviceModel || 'M_Z010_DEV_SQUARE_LIGHT'
- console.log('执行灯组能力(带参数):', {
- deviceCode: lampGroup.deviceCode,
- modelCode: modelCode,
- abilityKey: ability.abilityKey,
- paramValue: paramValue
- })
- await callAbility({
- objCode: lampGroup.deviceCode,
- objType: 2,
- modelCode: modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: paramValue
- })
- this.$message.success(`${ability.abilityName}执行成功`)
- // 重新加载单个灯组属性,必须传入modelCode
- await this.loadSingleLampGroupAttr(lampGroup.deviceCode, modelCode)
- } catch (error) {
- console.error('执行灯组能力失败:', error)
- this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
- } finally {
- ability.executing = false
- ability.executingValue = null
- }
- },
- // 加载单个灯组属性 - 必须传入deviceCode和modelCode两个参数
- async loadSingleLampGroupAttr(deviceCode, modelCode) {
- if (!modelCode) {
- console.error('loadSingleLampGroupAttr缺少modelCode参数!', { deviceCode, modelCode })
- this.$message.error('加载灯组属性失败:缺少modelCode参数')
- return
- }
- try {
- console.log('加载灯组属性 - 调用getObjAttr:', {
- objType: 2,
- deviceCode: deviceCode,
- modelCode: modelCode
- })
- // 必须传入modelCode参数
- const res = await getObjAttr(2, deviceCode, modelCode)
- this.$set(this.lampGroupAttrs, deviceCode, res.data || {})
- // 重新统计
- this.calculateLampGroupStats()
- console.log('灯组属性加载成功:', deviceCode)
- } catch (error) {
- console.error('加载灯组属性失败:', { deviceCode, modelCode, error })
- this.$message.error('加载灯组属性失败:' + error.message)
- }
- },
- // 刷新灯杆下所有灯组设备的属性
- async refreshLampPoleDevices(poleCode) {
- try {
- const lampGroups = this.getLampGroupCodesFromPole(poleCode)
- if (lampGroups.length > 0) {
- // 批量刷新所有灯组属性
- await this.loadDeviceAttrsBatch('M_Z010_DEV_SQUARE_LIGHT', this.lampGroupAttrs)
- // 重新统计
- this.calculateLampGroupStats()
- console.log(`已刷新灯杆 ${poleCode} 下的 ${lampGroups.length} 个灯组`)
- }
- } catch (error) {
- console.error('刷新灯杆设备失败:', error)
- this.$message.error('刷新灯杆设备失败:' + error.message)
- }
- },
- // 加载集中器设备列表
- async loadConcentratorDevices() {
- this.concentratorLoading = true
- try {
- const res = await getByCondition({
- subsystemCode: this.systemCode,
- deviceModel: 'M_Z010_DEV_SQUARE_CONCENTRATOR'
- })
- const deviceData = res.data || res.rows || []
- this.concentratorList = deviceData.map(device => ({
- ...device,
- detailTab: 'base'
- }))
- if (this.concentratorList.length === 0) {
- this.updateDeviceStats()
- return
- }
- // 批量加载所有集中器设备的属性
- if (this.concentratorList[0].deviceModel) {
- await this.loadDeviceAttrsBatch(this.concentratorList[0].deviceModel, this.concentratorAttrs)
- }
- // 更新设备统计
- this.updateDeviceStats()
- } catch (error) {
- this.$message.error('加载集中器设备失败')
- console.error('加载集中器设备错误:', error)
- } finally {
- this.concentratorLoading = false
- }
- },
- // 获取集中器单个属性值
- getConcentratorAttr(deviceCode, attrKey, attrGroup = 'State') {
- const attrs = this.concentratorAttrs[deviceCode]
- if (!attrs) return null
- // 如果指定了属性组,则从该组查找
- if (attrGroup && attrs[attrGroup]) {
- const attr = attrs[attrGroup].find(a => a.attrKey === attrKey)
- return attr ? attr.attrValue : null
- }
- // 如果没有指定组或该组不存在,默认从State组查找
- if (attrs.State) {
- const attr = attrs.State.find(a => a.attrKey === attrKey)
- return attr ? attr.attrValue : null
- }
- return null
- },
- // 获取集中器更新时间
- getConcentratorUpdateTime(deviceCode) {
- const attrs = this.concentratorAttrs[deviceCode]
- if (!attrs || !attrs.State || attrs.State.length === 0) return null
- const attrWithTime = attrs.State.find(a => a.updateTime)
- return attrWithTime ? attrWithTime.updateTime : null
- },
- // 获取电压状态
- getVoltageStatus(deviceCode) {
- const ua = parseFloat(this.getConcentratorAttr(deviceCode, 'ua'))
- const ub = parseFloat(this.getConcentratorAttr(deviceCode, 'ub'))
- const uc = parseFloat(this.getConcentratorAttr(deviceCode, 'uc'))
- const isNormal = (voltage) => voltage >= 198 && voltage <= 242
- const allNormal = isNormal(ua) && isNormal(ub) && isNormal(uc)
- if (allNormal) {
- return { type: 'success', text: '正常' }
- } else {
- const abnormalPhases = []
- if (!isNormal(ua)) abnormalPhases.push('A')
- if (!isNormal(ub)) abnormalPhases.push('B')
- if (!isNormal(uc)) abnormalPhases.push('C')
- return { type: 'warning', text: `${abnormalPhases.join('/')}相异常` }
- }
- },
- // 获取电压样式类
- getVoltageClass(voltageStr) {
- const voltage = parseFloat(voltageStr)
- if (!voltage || isNaN(voltage)) return ''
- if (voltage >= 198 && voltage <= 242) {
- return 'voltage-normal'
- } else if (voltage < 198) {
- return 'voltage-low'
- } else {
- return 'voltage-high'
- }
- },
- // 更新设备统计
- updateDeviceStats() {
- const lampOnline = this.lampPoleList.filter(d => d.deviceStatus === 1).length
- const lampOffline = this.lampPoleList.filter(d => d.deviceStatus !== 1).length
- const concentratorOnline = this.concentratorList.filter(d => d.deviceStatus === 1).length
- const concentratorOffline = this.concentratorList.filter(d => d.deviceStatus !== 1).length
- this.deviceStats.total = this.lampPoleList.length + this.concentratorList.length
- this.deviceStats.online = lampOnline + concentratorOnline
- this.deviceStats.offline = lampOffline + concentratorOffline
- this.deviceStats.concentrators = this.concentratorList.length
- this.deviceStats.lamps = this.totalLampCount
- },
- // 处理集中器行点击
- handleConcentratorClick(row, column, event) {
- if (column && column.type === 'expand') {
- return
- }
- this.$refs.concentratorTable?.toggleRowExpansion(row)
- },
- // 处理快速操作
- async handleQuickAction(action) {
- action.loading = true
- try {
- switch (action.key) {
- case 'syncProject':
- await this.executeAbilityByKey('GetProjectList', null)
- break
- case 'syncDevice':
- await this.executeAbilityByKey('GetDeviceList', null)
- break
- case 'allOn':
- await this.executeAbilityByKey('lightControlAll', "1")
- break
- case 'allOff':
- await this.executeAbilityByKey('lightControlAll', "0")
- break
- }
- this.$message.success(`${action.name}成功`)
- } catch (error) {
- this.$message.error(`${action.name}失败`)
- } finally {
- action.loading = false
- }
- },
- // 根据key执行能力
- async executeAbilityByKey(abilityKey, param) {
- const ability = this.systemAbilities.find(a => a.abilityKey === abilityKey)
- if (ability) {
- await callAbility({
- objCode: this.systemCode,
- objType: 3,
- modelCode: this.systemInfo.modelCode,
- abilityKey: ability.abilityKey,
- abilityParam: param
- })
- // 刷新数据
- if (abilityKey === 'lightControlAll') {
- // 全部开关灯后,批量刷新所有灯组设备属性
- if (this.lampGroupList.length > 0) {
- await this.loadDeviceAttrsBatch('M_Z010_DEV_SQUARE_LIGHT', this.lampGroupAttrs)
- this.calculateLampGroupStats()
- }
- } else if (abilityKey === 'GetDeviceList') {
- await this.loadLampPoleList()
- await this.loadConcentratorDevices()
- }
- } else {
- throw new Error(`能力 ${abilityKey} 未找到`)
- }
- },
- // 标签页切换
- handleTabClick(tab) {
- if (tab.name === 'callLogs' && this.callLogList.length === 0) {
- this.queryCallLogs()
- } else if (tab.name === 'eventLogs' && this.eventLogList.length === 0) {
- this.queryEventLogs()
- }
- },
- // 查询调用日志
- async queryCallLogs() {
- this.logLoading = true
- try {
- const params = {
- modelCodes: this.allModelCodes,
- pageNum: this.callLogQuery.pageNum,
- pageSize: this.callLogQuery.pageSize
- }
- if (this.callLogQuery.dateRange && this.callLogQuery.dateRange.length === 2) {
- params.startRecTime = this.callLogQuery.dateRange[0]
- params.endRecTime = this.callLogQuery.dateRange[1]
- }
- if (this.callLogQuery.abilityKey) {
- params.abilityKey = this.callLogQuery.abilityKey
- }
- if (this.callLogQuery.callStatus !== '') {
- params.callStatus = this.callLogQuery.callStatus
- }
- const res = await listCallLog(params)
- this.callLogList = res.rows || []
- this.callLogTotal = res.total || 0
- } catch (error) {
- this.$message.error('查询调用日志失败')
- } finally {
- this.logLoading = false
- }
- },
- // 重置调用日志查询
- resetCallLogQuery() {
- this.initDateRange()
- this.callLogQuery.abilityKey = ''
- this.callLogQuery.callStatus = ''
- this.callLogQuery.pageNum = 1
- this.queryCallLogs()
- },
- // 查询事件日志
- async queryEventLogs() {
- this.eventLogLoading = true
- try {
- const params = {
- modelCodes: this.allModelCodes,
- pageNum: this.eventLogQuery.pageNum,
- pageSize: this.eventLogQuery.pageSize
- }
- if (this.eventLogQuery.dateRange && this.eventLogQuery.dateRange.length === 2) {
- params.startRecTime = this.eventLogQuery.dateRange[0]
- params.endRecTime = this.eventLogQuery.dateRange[1]
- }
- if (this.eventLogQuery.eventKey) {
- params.eventKey = this.eventLogQuery.eventKey
- }
- const res = await listEventLog(params)
- this.eventLogList = res.rows || []
- this.eventLogTotal = res.total || 0
- } catch (error) {
- this.$message.error('查询事件日志失败')
- } finally {
- this.eventLogLoading = false
- }
- },
- // 重置事件日志查询
- resetEventLogQuery() {
- this.initDateRange()
- this.eventLogQuery.eventKey = ''
- this.eventLogQuery.pageNum = 1
- this.queryEventLogs()
- },
- // 格式化调用状态
- formatCallStatus(status) {
- const statusMap = {
- 0: '成功',
- 1: '进行中',
- 2: '失败'
- }
- return statusMap[status] || '未知'
- },
- // 获取调用状态类型
- getCallStatusType(status) {
- const typeMap = {
- 0: 'success',
- 1: 'warning',
- 2: 'danger'
- }
- return typeMap[status] || 'info'
- },
- // 查看调用日志详情
- async handleCallLogDetail(row) {
- try {
- const res = await getCallLog(row.id)
- this.callLogDetailData = res.data
- this.callLogDetailDialog = true
- } catch (error) {
- this.$message.error('获取调用日志详情失败')
- }
- },
- // 查看事件日志详情
- async handleEventLogDetail(row) {
- try {
- const res = await getEventLog(row.id)
- this.eventLogDetailData = res.data
- this.eventLogDetailDialog = true
- } catch (error) {
- this.$message.error('获取事件日志详情失败')
- }
- },
- // 打开编辑系统信息对话框
- handleEditSystem() {
- // 填充表单数据
- this.editSystemForm = {
- id: this.systemInfo.id,
- systemCode: this.systemInfo.systemCode,
- systemName: this.systemInfo.systemName,
- shortName: this.systemInfo.shortName,
- manFacturer: this.systemInfo.manFacturer,
- contactPerson: this.systemInfo.contactPerson,
- contactNumber: this.systemInfo.contactNumber,
- maintainerPerson: this.systemInfo.maintainerPerson,
- maintainerNumber: this.systemInfo.maintainerNumber,
- descr: this.systemInfo.descr,
- modelCode: this.systemInfo.modelCode
- }
- this.editSystemDialogVisible = true
- },
- // 提交编辑系统信息
- async submitEditSystem() {
- this.$refs.editSystemForm.validate(async (valid) => {
- if (!valid) {
- return
- }
- this.editSystemSubmitting = true
- try {
- await updateSubsystem(this.editSystemForm)
- this.$message.success('保存成功')
- this.editSystemDialogVisible = false
- // 重新加载系统信息
- await this.loadSystemInfo()
- } catch (error) {
- this.$message.error('保存失败:' + error.message)
- } finally {
- this.editSystemSubmitting = false
- }
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .app-container {
- padding: 20px;
- background: #f5f7fa;
- min-height: calc(100vh - 84px);
- }
- .system-info-card {
- margin-bottom: 20px;
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- .title {
- font-size: 18px;
- font-weight: bold;
- i {
- margin-right: 8px;
- color: #f39c12;
- }
- }
- }
- .info-group {
- position: relative;
- .group-title {
- font-size: 14px;
- font-weight: bold;
- color: #606266;
- margin-bottom: 12px;
- padding-bottom: 6px;
- border-bottom: 1px solid #EBEEF5;
- }
- }
- .info-item {
- margin-bottom: 12px;
- font-size: 14px;
- label {
- color: #909399;
- margin-right: 8px;
- display: inline-block;
- min-width: 80px;
- }
- .code {
- font-family: monospace;
- color: #f39c12;
- background: #fff9e6;
- padding: 2px 6px;
- border-radius: 3px;
- }
- .stat-number {
- color: #409EFF;
- font-weight: bold;
- font-size: 16px;
- }
- }
- }
- .detail-card {
- .tab-content {
- min-height: 400px;
- }
- .quick-actions {
- margin-bottom: 30px;
- .section-title {
- margin: 0 0 15px 0;
- color: #303133;
- display: flex;
- align-items: center;
- i {
- margin-right: 8px;
- color: #409EFF;
- }
- }
- .action-btn {
- width: 100%;
- height: 60px;
- font-size: 14px;
- border-radius: 8px;
- transition: all 0.3s;
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
- }
- }
- }
- .monitor-panel {
- margin-bottom: 30px;
- .section-title {
- margin: 20px 0 15px 0;
- color: #303133;
- display: flex;
- align-items: center;
- i {
- margin-right: 8px;
- color: #409EFF;
- }
- }
- .monitor-card {
- background: #fff;
- border: 1px solid #EBEEF5;
- border-radius: 8px;
- padding: 20px;
- display: flex;
- align-items: center;
- transition: all 0.3s;
- cursor: pointer;
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- }
- .monitor-icon {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 24px;
- margin-right: 15px;
- &.online {
- background: #f0f9ff;
- color: #67C23A;
- }
- &.offline {
- background: #fef0f0;
- color: #F56C6C;
- }
- &.lamps {
- background: #fff9e6;
- color: #f39c12;
- }
- &.concentrator {
- background: #f4e4ff;
- color: #9b59b6;
- }
- &.power {
- background: #f0f2f5;
- color: #909399;
- }
- }
- .monitor-info {
- .monitor-value {
- font-size: 24px;
- font-weight: bold;
- color: #303133;
- }
- .monitor-label {
- font-size: 14px;
- color: #909399;
- margin-top: 4px;
- }
- }
- }
- }
- .concentrator-panel {
- margin-top: 30px;
- .section-title {
- margin: 20px 0 15px 0;
- color: #303133;
- display: flex;
- align-items: center;
- i {
- margin-right: 8px;
- color: #9b59b6;
- }
- }
- }
- .concentrator-detail {
- padding: 25px;
- background: #f5f7fa;
- h5 {
- margin: 0 0 20px 0;
- color: #303133;
- font-size: 16px;
- font-weight: 600;
- display: flex;
- align-items: center;
- i {
- margin-right: 8px;
- color: #409EFF;
- }
- }
- .param-group {
- background: #fff;
- border-radius: 8px;
- padding: 15px;
- margin-bottom: 20px;
- border: 1px solid #e4e7ed;
- .group-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- padding-bottom: 10px;
- border-bottom: 2px solid #f0f2f5;
- .group-title {
- font-size: 14px;
- font-weight: 600;
- color: #606266;
- }
- }
- .param-card {
- background: #fafbfc;
- border-radius: 6px;
- padding: 15px;
- border: 1px solid #ebeef5;
- transition: all 0.3s;
- position: relative;
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 2px 8px rgba(0,0,0,0.08);
- }
- &.phase-a {
- border-left: 4px solid #f39c12;
- }
- &.phase-b {
- border-left: 4px solid #27ae60;
- }
- &.phase-c {
- border-left: 4px solid #e74c3c;
- }
- &.voltage-normal {
- background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
- .value {
- color: #52c41a;
- }
- }
- &.voltage-low {
- background: linear-gradient(135deg, #fff7e6 0%, #ffe7ba 100%);
- .value {
- color: #fa8c16;
- }
- }
- &.voltage-high {
- background: linear-gradient(135deg, #fff1f0 0%, #ffccc7 100%);
- .value {
- color: #f5222d;
- }
- }
- .phase-label {
- font-size: 12px;
- color: #909399;
- margin-bottom: 8px;
- font-weight: 600;
- }
- .param-label {
- font-size: 12px;
- color: #909399;
- margin-bottom: 8px;
- }
- .param-value {
- display: flex;
- align-items: baseline;
- .value {
- font-size: 24px;
- font-weight: 600;
- color: #303133;
- margin-right: 5px;
- }
- .unit {
- font-size: 14px;
- color: #606266;
- }
- }
- .voltage-indicator {
- margin-top: 8px;
- .normal-range {
- font-size: 11px;
- color: #909399;
- }
- }
- }
- }
- .update-time {
- text-align: right;
- font-size: 12px;
- color: #909399;
- margin-top: 10px;
- i {
- margin-right: 5px;
- }
- }
- }
- // 集中器描述列表样式
- .concentrator-descriptions {
- ::v-deep .el-descriptions__label {
- background: #fafafa;
- color: #606266;
- font-weight: 500;
- padding: 10px 12px;
- width: 120px;
- }
- ::v-deep .el-descriptions__content {
- background: #fff;
- color: #303133;
- padding: 10px 12px;
- }
- ::v-deep .el-descriptions-item__cell {
- border-color: #e4e7ed;
- }
- }
- .device-image {
- h5 {
- margin: 15px 0 10px 0;
- color: #303133;
- font-size: 14px;
- font-weight: 600;
- }
- .el-image {
- border-radius: 4px;
- border: 1px solid #e4e7ed;
- overflow: hidden;
- }
- }
- .attr-section {
- margin-bottom: 30px;
- .section-title {
- margin: 20px 0 15px 0;
- color: #303133;
- i {
- margin-right: 8px;
- color: #409EFF;
- }
- }
- .url-text {
- color: #409EFF;
- font-family: monospace;
- }
- }
- .project-section {
- margin-bottom: 20px;
- h5 {
- margin: 15px 0 10px 0;
- color: #606266;
- font-size: 14px;
- }
- }
- .device-stats {
- margin-bottom: 20px;
- .stat-item {
- text-align: center;
- padding: 20px;
- background: #fff;
- border: 1px solid #EBEEF5;
- border-radius: 4px;
- .stat-value {
- font-size: 28px;
- font-weight: bold;
- color: #303133;
- margin-bottom: 8px;
- }
- .stat-label {
- font-size: 14px;
- color: #909399;
- }
- &.online .stat-value {
- color: #67C23A;
- }
- &.offline .stat-value {
- color: #F56C6C;
- }
- &.lamps .stat-value {
- color: #f39c12;
- }
- &.controllers .stat-value {
- color: #409EFF;
- }
- }
- }
- // 灯杆主表格样式
- .lamp-pole-main-table {
- ::v-deep .el-table__expanded-cell {
- padding: 15px 20px;
- background: #fafbfc;
- }
- }
- // 智慧灯杆展示样式
- .lamp-pole-detail {
- padding: 15px 20px;
- background: #f9fafc;
- .lamp-group-table {
- border: 1px solid #e4e7ed;
- border-radius: 4px;
- overflow: hidden;
- ::v-deep .el-table__header {
- background: #f5f7fa;
- th {
- background: #f5f7fa !important;
- font-weight: 600;
- color: #606266;
- padding: 12px 8px;
- }
- }
- ::v-deep .el-table__body {
- td {
- padding: 12px 8px;
- }
- }
- // 移除表格后多余空白
- ::v-deep .el-table__empty-block {
- display: none;
- }
- // 优化展开行样式
- ::v-deep .el-table__expanded-cell {
- padding: 15px 20px;
- }
- }
- }
- .lamp-group-detail {
- padding: 15px;
- background: #f5f7fa;
- // Tab标签样式优化
- ::v-deep .el-tabs__header {
- margin-bottom: 15px;
- }
- ::v-deep .el-tabs__content {
- padding: 0;
- }
- .attr-group-title {
- margin: 15px 0 10px 0;
- color: #303133;
- font-size: 14px;
- font-weight: 600;
- padding-left: 10px;
- border-left: 3px solid #409EFF;
- }
- // 优化描述列表样式
- .lamp-group-descriptions {
- ::v-deep .el-descriptions__label {
- background: #fafafa;
- color: #606266;
- font-weight: 500;
- padding: 10px 12px;
- }
- ::v-deep .el-descriptions__content {
- background: #fff;
- color: #303133;
- padding: 10px 12px;
- }
- ::v-deep .el-descriptions-item__cell {
- border-color: #e4e7ed;
- }
- }
- // 灯组图片样式
- .el-image {
- border-radius: 4px;
- border: 1px solid #e4e7ed;
- overflow: hidden;
- }
- .update-time {
- text-align: right;
- font-size: 12px;
- color: #909399;
- padding-top: 10px;
- margin-top: 10px;
- border-top: 1px solid #e4e7ed;
- i {
- margin-right: 5px;
- }
- }
- .ability-panel {
- padding: 10px 0;
- min-height: 200px;
- .ability-card {
- background: #fff;
- border: 1px solid #e4e7ed;
- border-radius: 8px;
- padding: 15px;
- margin-bottom: 15px;
- transition: all 0.3s;
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- border-color: #409EFF;
- }
- .ability-header {
- display: flex;
- align-items: center;
- margin-bottom: 8px;
- font-size: 14px;
- font-weight: 600;
- color: #303133;
- i {
- margin-right: 8px;
- color: #409EFF;
- font-size: 16px;
- }
- }
- .ability-desc {
- font-size: 12px;
- color: #909399;
- margin-bottom: 12px;
- line-height: 1.5;
- min-height: 20px;
- }
- .light-control-buttons {
- display: flex;
- gap: 8px;
- .el-button {
- flex: 1;
- }
- }
- .ability-buttons {
- width: 100%;
- display: flex;
- .el-button {
- flex: 1;
- }
- }
- // 单个按钮样式
- > .el-button {
- margin-top: 10px;
- }
- }
- }
- }
- .log-filter {
- margin-bottom: 20px;
- padding: 15px;
- background: #f5f7fa;
- border-radius: 4px;
- }
- }
- .execute-result {
- .result-data {
- margin-top: 20px;
- pre {
- background: #f5f7fa;
- padding: 15px;
- border-radius: 4px;
- overflow: auto;
- max-height: 400px;
- font-family: monospace;
- font-size: 12px;
- }
- }
- }
- .image-slot {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- background: #f5f7fa;
- color: #909399;
- font-size: 30px;
- }
- .dialog-footer {
- text-align: right;
- }
- .device-detail,
- .lamp-pole-detail,
- .lamp-group-detail {
- padding: 20px;
- background: #f9fafc;
- border-radius: 4px;
- ::v-deep .el-tabs__header {
- background: #fff;
- border: 1px solid #e4e7ed;
- border-radius: 4px;
- margin-bottom: 15px;
- padding: 0 10px;
- }
- ::v-deep .el-tabs__item {
- height: 40px;
- line-height: 40px;
- font-size: 14px;
- &.is-active {
- color: #409EFF;
- font-weight: bold;
- }
- }
- ::v-deep .el-tabs__nav-wrap::after {
- display: none;
- }
- }
- // 密码字段样式
- .password-field {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- .password-toggle-icon {
- cursor: pointer;
- color: #909399;
- font-size: 16px;
- transition: color 0.3s;
- &:hover {
- color: #409EFF;
- }
- }
- }
- </style>
|