OCPPOperations.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. <template>
  2. <div class="card-container">
  3. <div class="card-content">
  4. <div class="ocpp-content">
  5. <div class="section-title">Selected Stations</div>
  6. <div class="filter-view">
  7. <el-select
  8. class="filter-view-item"
  9. v-model="listQuery.pageVo.providerPk"
  10. placeholder="Service Provider"
  11. @change="changeSite"
  12. clearable>
  13. <el-option
  14. v-for="(item, index) in providerOptions"
  15. :key="index"
  16. :label="item.providerName"
  17. :value="item.providerPk"/>
  18. </el-select>
  19. <el-select
  20. class="filter-view-item"
  21. clearable
  22. filterable
  23. remote
  24. v-model="listQuery.pageVo.sitePk"
  25. :remote-method="filterSite"
  26. :loading="siteLoading"
  27. placeholder="Site Name"
  28. @change="changeSite">
  29. <el-option
  30. v-for="(item, index) in siteList"
  31. :key="index"
  32. :label="item.siteName"
  33. :value="item.sitePk"/>
  34. </el-select>
  35. </div>
  36. <div class="flexc" style="padding: 20px 0; justify-content: flex-end;">
  37. <el-button
  38. type="primary"
  39. @click="selectNone">
  40. Select None
  41. </el-button>
  42. <el-button
  43. style="margin-left: 15px;"
  44. type="primary"
  45. @click="selectAll">
  46. Select All
  47. </el-button>
  48. </div>
  49. <el-table
  50. style="width: 100%;min-height: 50vh;"
  51. v-loading="listLoading"
  52. :data="stationList">
  53. <el-table-column
  54. :label="item.label"
  55. align="center"
  56. class-name="fixed-width"
  57. v-for="(item, index) in columns"
  58. :key="index"
  59. :width="item.width">
  60. <template slot-scope="{row, $index}">
  61. <el-checkbox
  62. v-if="item.action"
  63. v-model="row[item.key]"
  64. @change="checked => changeRow($index, checked)"/>
  65. <span v-else>{{ row[item.key] }}</span>
  66. </template>
  67. </el-table-column>
  68. </el-table>
  69. <div class="right">
  70. <pagination
  71. v-show="total > 0"
  72. :total="total"
  73. :page.sync="listQuery.pageNo"
  74. :limit.sync="listQuery.pageSize"
  75. @pagination="handlePageChange" />
  76. </div>
  77. </div>
  78. <div class="ocpp-content">
  79. <el-form
  80. :model="operationForm"
  81. :rules="rule"
  82. ref="operationForm"
  83. label-width="200px"
  84. label-position="left">
  85. <div class="section-title">OCPP Operation</div>
  86. <el-form-item
  87. label="Operation:"
  88. prop="operation">
  89. <el-select
  90. class="flex-item"
  91. v-model="operationForm.operation"
  92. @change="changeOperation">
  93. <el-option
  94. v-for="(item, index) in operationList"
  95. :key="index"
  96. :label="item.name"
  97. :value="index"
  98. v-if="item.api"/>
  99. </el-select>
  100. </el-form-item>
  101. <div class="section-title" style="padding-top: 20px;" v-loading="paramLoading">Parameters</div>
  102. <el-form-item
  103. label="Filter Type:"
  104. prop="filterType"
  105. v-if="paramsOption.filterTypeOption.length > 0">
  106. <el-select
  107. v-model="operationForm.filterType"
  108. class="flex-item"
  109. @change="changeFilterType">
  110. <el-option
  111. v-for="(item, index) in paramsOption.filterTypeOption"
  112. :key="index"
  113. :label="item.name"
  114. :value="item.value"/>
  115. </el-select>
  116. </el-form-item>
  117. <el-form-item
  118. label="Charging Profile ID:"
  119. prop="chargingProfilePk"
  120. v-if="paramsOption.needChargingProfile">
  121. <el-select
  122. v-model="operationForm.chargingProfilePk"
  123. class="flex-item">
  124. <el-option
  125. v-for="(item, index) in paramsOption.chargingProfileOption"
  126. :key="index"
  127. :label="item.value + ' (' + item.name + ')'"
  128. :value="item.value"/>
  129. </el-select>
  130. </el-form-item>
  131. <el-form-item
  132. label="Connector ID:"
  133. prop="connectorId"
  134. v-if="paramsOption.needConnector">
  135. <el-select
  136. v-model="operationForm.connectorId"
  137. class="flex-item">
  138. <el-option
  139. v-for="(item, index) in paramsOption.connectorIds"
  140. :key="index"
  141. :label="item"
  142. :value="item"/>
  143. </el-select>
  144. </el-form-item>
  145. <el-form-item
  146. label="Transaction ID:"
  147. prop="transactionId"
  148. v-if="paramsOption.needActConnector">
  149. <el-select
  150. v-model="operationForm.transactionId"
  151. class="flex-item">
  152. <el-option
  153. v-for="(item, index) in paramsOption.connectorActIds"
  154. :key="index"
  155. :label="item"
  156. :value="item"/>
  157. </el-select>
  158. </el-form-item>
  159. <el-form-item
  160. label="Mobile Number:"
  161. prop="idTagInfo"
  162. v-if="paramsOption.idTagAll.length > 0">
  163. <el-select
  164. v-model="operationForm.idTagInfo"
  165. filterable
  166. :remote-method="filterMobile"
  167. class="flex-item"
  168. :loading="tagLoading">
  169. <el-option
  170. v-for="(item, index) in paramsOption.idTags"
  171. :key="index"
  172. :label="item.name"
  173. :value="item.value"/>
  174. </el-select>
  175. </el-form-item>
  176. <el-form-item
  177. label="Availability Type:"
  178. prop="availType"
  179. v-if="paramsOption.availTypeOption.length > 0">
  180. <el-select
  181. v-model="operationForm.availType"
  182. class="flex-item"
  183. placeholder="Select">
  184. <el-option
  185. v-for="(item, index) in paramsOption.availTypeOption"
  186. :key="index"
  187. :label="item.name"
  188. :value="item.value"/>
  189. </el-select>
  190. </el-form-item>
  191. <el-form-item
  192. label="Charging Profile Purpose:"
  193. prop="chargingProfilePurpose"
  194. v-if="paramsOption.needChargingProfilePurpose">
  195. <el-select
  196. v-model="operationForm.chargingProfilePurpose"
  197. class="flex-item">
  198. <el-option
  199. v-for="(item, index) in paramsOption.chargingPurposeOption"
  200. :key="index"
  201. :label="item.name"
  202. :value="item.value"/>
  203. </el-select>
  204. </el-form-item>
  205. <el-form-item
  206. label="Stack Level (integer):"
  207. v-if="paramsOption.needChargingProfilePurpose">
  208. <el-input
  209. v-model="operationForm.stackLevel"
  210. class="flex-item"/>
  211. </el-form-item>
  212. <el-form-item
  213. label="Key Type:"
  214. prop="keyType"
  215. v-if="paramsOption.keyTypeOption.length > 0">
  216. <el-select
  217. v-model="operationForm.keyType"
  218. class="flex-item"
  219. placeholder="Select"
  220. @change="changeKeyType">
  221. <el-option
  222. v-for="(item, index) in paramsOption.keyTypeOption"
  223. :key="index"
  224. :label="item.name"
  225. :value="item.value"/>
  226. </el-select>
  227. </el-form-item>
  228. <div
  229. class="multi-select"
  230. v-if="paramsOption.confKeyOption.length > 0 && paramsOption.selectMultConfig">
  231. <span class="note-span">Info: If none selected, the charge point returns a list of all configuration settings.</span>
  232. <el-form-item
  233. label="Configuration Key:">
  234. <el-select
  235. v-model="operationForm.confKeyList"
  236. class="flex-item"
  237. style="align-items: flex-start;"
  238. placeholder="Select multiple"
  239. :multiple="true">
  240. <el-option
  241. v-for="(item, index) in paramsOption.confKeyOption"
  242. :key="index"
  243. :label="item.name"
  244. :value="item.value"/>
  245. </el-select>
  246. </el-form-item>
  247. </div>
  248. <el-form-item
  249. label="Configuration Key:"
  250. prop="confKey"
  251. v-else-if="paramsOption.confKeyOption.length > 0 && operationForm.keyType == 'PREDEFINED'">
  252. <el-select
  253. v-model="operationForm.confKey"
  254. class="flex-item"
  255. placeholder="Select">
  256. <el-option
  257. v-for="(item, index) in paramsOption.confKeyOption"
  258. :key="index"
  259. :label="item.name"
  260. :value="item.value"/>
  261. </el-select>
  262. </el-form-item>
  263. <el-form-item
  264. label="Custom Configuration Key:"
  265. prop="customConfKey"
  266. label-width="205px"
  267. v-if="paramsOption.keyTypeOption.length > 0 && operationForm.keyType == 'CUSTOM'">
  268. <el-input
  269. v-model="operationForm.customConfKey"
  270. class="flex-item"/>
  271. </el-form-item>
  272. <el-form-item
  273. label="Value:"
  274. v-if="paramsOption.keyTypeOption.length > 0">
  275. <el-input
  276. v-model="operationForm.value"
  277. class="flex-item"/>
  278. </el-form-item>
  279. <el-form-item
  280. label="Reset Type:"
  281. prop="resetType"
  282. v-if="paramsOption.resetTypeOption.length > 0">
  283. <el-select
  284. class="flex-item"
  285. v-model="operationForm.resetType">
  286. <el-option
  287. v-for="(item, index) in paramsOption.resetTypeOption"
  288. :key="index"
  289. :label="item.name"
  290. :value="item.value"/>
  291. </el-select>
  292. </el-form-item>
  293. <template v-if="operationInfo.value == 'UF'">
  294. <el-form-item
  295. label="Location(directory URI):"
  296. prop="location">
  297. <el-input
  298. v-model="operationForm.location"
  299. class="flex-item"/>
  300. </el-form-item>
  301. <el-form-item
  302. label="Retries(integer):">
  303. <el-input
  304. v-model="operationForm.retries"
  305. class="flex-item"/>
  306. </el-form-item>
  307. <el-form-item
  308. label="Retry Interval(integer):">
  309. <el-input
  310. v-model="operationForm.retryInterval"
  311. class="flex-item"/>
  312. </el-form-item>
  313. <el-form-item
  314. label="Retrieve Date/Time:"
  315. prop="retrieve">
  316. <el-date-picker
  317. v-model="operationForm.retrieve"
  318. type="datetime"
  319. format="yyyy-MM-dd HH:mm"
  320. value-format="yyyy-MM-dd HH:mm"
  321. default-time="12:00:00"
  322. class="flex-item"/>
  323. </el-form-item>
  324. </template>
  325. <div class="perform">
  326. <el-button
  327. style="margin-left: 15px;"
  328. type="primary"
  329. :loading="btnLoading"
  330. @click="perform">
  331. Perform
  332. </el-button>
  333. </div>
  334. </el-form>
  335. </div>
  336. </div>
  337. </div>
  338. </template>
  339. <script>
  340. import Pagination from '@/components/Pagination'
  341. import site from '../../http/api/site'
  342. import ocpp from '../../http/api/ocpp'
  343. import util from './operationUtil'
  344. import provider from '../../http/api/provider'
  345. const NOT_SPECIFIC = 'Not for a specific connector'
  346. export default {
  347. data() {
  348. return {
  349. tagLoading: false,
  350. listLoading: false,
  351. siteLoading: false,
  352. paramLoading: false,
  353. btnLoading: false,
  354. siteList: [],
  355. siteKeyword: '',
  356. stationList: [],
  357. providerOptions: [],
  358. total: 0,
  359. listQuery: {
  360. pageNo: 1,
  361. pageSize: 10,
  362. pageVo: {
  363. sitePk: '',
  364. stationId: '',
  365. providerPk: ''
  366. }
  367. },
  368. operationInfo: {},
  369. operationList: ocpp.getOperationList(),
  370. operationForm: {
  371. value: '',
  372. confKey: '',
  373. keyType: '',
  374. resetType: '',
  375. operation: '',
  376. availType: '',
  377. idTagInfo: '',
  378. filterType: '',
  379. location: "",
  380. retrieve: "",
  381. retries: "",
  382. retryInterval: "",
  383. confKeyList: [],
  384. stationIds: [],
  385. stackLevel: '',
  386. connectorId: '',
  387. transactionId: '',
  388. customConfKey: '',
  389. chargingProfilePk: '',
  390. chargingProfilePurpose: undefined
  391. },
  392. paramsOption: {
  393. idTags: [],
  394. idTagAll: [],
  395. connectorIds: [],
  396. connectorActIds: [],
  397. needConnector: false,
  398. needActConnector: false,
  399. selectMultConfig: false,
  400. confKeyOption: [],
  401. keyTypeOption: [],
  402. availTypeOption: [],
  403. resetTypeOption: [],
  404. filterTypeOption: [],
  405. needCustomCoinfig: false,
  406. needChargingProfile: false,
  407. chargingProfileOption: [],
  408. chargingPurposeOption: [],
  409. needChargingProfilePurpose: false
  410. },
  411. rule: {
  412. idTagInfo: {
  413. required: true,
  414. trigger: 'change',
  415. message: 'Please select ID tag'
  416. },
  417. operation: {
  418. required: true,
  419. trigger: 'change',
  420. message: 'Please select operation'
  421. },
  422. confKey: {
  423. required: true,
  424. trigger: 'change',
  425. message: 'Please select configuration key'
  426. },
  427. keyType: {
  428. required: true,
  429. trigger: 'change',
  430. message: 'Please select key type'
  431. },
  432. resetType: {
  433. required: true,
  434. trigger: 'change',
  435. message: 'Please select reset type'
  436. },
  437. availType: {
  438. required: true,
  439. trigger: 'change',
  440. message: 'Please select availability type'
  441. },
  442. filterType: {
  443. required: true,
  444. trigger: 'change',
  445. message: 'Please select filter type'
  446. },
  447. connectorId: {
  448. required: true,
  449. trigger: 'change',
  450. message: 'Please select a connector ID'
  451. },
  452. confKeyList: {
  453. required: true,
  454. trigger: 'change',
  455. message: 'Please select at last least 1 config key'
  456. },
  457. transactionId: {
  458. required: true,
  459. trigger: 'change',
  460. message: 'Please select a active transaction ID'
  461. },
  462. customConfKey: {
  463. required: true,
  464. trigger: 'blur',
  465. message: 'Please input custom configuration key'
  466. },
  467. chargingProfilePk: {
  468. required: true,
  469. trigger: 'change',
  470. message: 'Please select Charging Profile ID'
  471. },
  472. chargingProfilePurpose: {
  473. required: true,
  474. trigger: 'change',
  475. message: 'Please select Charging Profile Purpose'
  476. },
  477. location: {
  478. required: true,
  479. trigger: 'blur',
  480. message: 'Please input location'
  481. },
  482. retrieve: {
  483. required: true,
  484. trigger: 'change',
  485. message: 'Please select retrieve date time'
  486. }
  487. },
  488. columns: [{
  489. key: 'siteName',
  490. label: 'Site Name'
  491. }, {
  492. key: 'stationId',
  493. label: 'Station ID'
  494. }, {
  495. key: 'lastHeartbeat',
  496. label: 'Last Heartbeat'
  497. }, {
  498. key: 'checked',
  499. label: 'Select',
  500. action: true,
  501. width: 70
  502. }],
  503. searchTag: ""
  504. }
  505. },
  506. components: { Pagination },
  507. created() {
  508. this.getAllProvider();
  509. this.getAllSite();
  510. },
  511. methods: {
  512. filterSite(word) {
  513. this.siteKeyword = word;
  514. this.siteLoading = true;
  515. this.getAllSite();
  516. },
  517. getAllSite() {
  518. site.getAllSiteList({siteName: this.siteKeyword}).then(res => {
  519. var list = []
  520. /*if (this.siteKeyword == '') {
  521. list = [{siteName: 'All', sitePk: ''}]
  522. }*/
  523. if (res.data && res.data.length > 0) {
  524. this.siteList = list.concat(res.data)
  525. } else {
  526. this.siteList = []
  527. }
  528. this.siteLoading = false;
  529. this.changeSite();
  530. }).catch(err => {
  531. this.siteLoading = false;
  532. this.$message({
  533. message: error,
  534. type: 'error'
  535. })
  536. });
  537. },
  538. getAllProvider() {
  539. provider.getAllServiceProvider().then(res => {
  540. if (res.data && res.data.length > 0) {
  541. this.providerOptions = res.data
  542. }
  543. });
  544. },
  545. changeSite() {
  546. this.listLoading = true;
  547. const res = ocpp.getStationPages(this.listQuery).then(res => {
  548. if (res.success) {
  549. this.total = res.total;
  550. this.stationList = res.data;
  551. }
  552. }).catch(err => {
  553. this.$message({
  554. message: error,
  555. type: 'error'
  556. })
  557. }).finally(() => {
  558. this.listLoading = false;
  559. });
  560. },
  561. async changeOperation(index) {
  562. this.paramLoading = true;
  563. this.operationForm = {
  564. operation: this.operationForm.operation,
  565. value: '',
  566. confKey: '',
  567. keyType: '',
  568. resetType: '',
  569. availType: '',
  570. idTagInfo: '',
  571. filterType: '',
  572. location: "",
  573. retrieve: "",
  574. retries: "",
  575. retryInterval: "",
  576. confKeyList: [],
  577. stationIds: [],
  578. stackLevel: '',
  579. connectorId: '',
  580. transactionId: '',
  581. customConfKey: '',
  582. chargingProfilePk: '',
  583. chargingProfilePurpose: undefined
  584. }
  585. this.operationInfo = this.operationList[index];
  586. this.selectNone();
  587. this.paramsOption = await util.getOperationParams(this.operationInfo, /*this.getCheckedStation()*/);
  588. //console.log('-----', paramsOption);
  589. this.paramLoading = false;
  590. this.$refs['operationForm'].clearValidate()
  591. },
  592. async changeRow(index, checked) {
  593. if (checked && this.operationInfo.singleStation) {
  594. this.selectNone();
  595. this.stationList[index].checked = true;
  596. }
  597. this.operationForm.stationIds = this.getCheckedStation();
  598. if (this.paramsOption.needConnector) {
  599. this.operationForm.connectorId = '';
  600. let list = await util.getConnectorIds(this.operationForm.stationIds);
  601. if (this.operationInfo.value == 'RSTART') {
  602. list.unshift(NOT_SPECIFIC)
  603. }
  604. this.paramsOption.connectorIds = list
  605. }
  606. if (this.paramsOption.needActConnector) {
  607. this.operationForm.transactionId = '';
  608. let list = await util.getActiveConnectorIds(this.operationForm.stationIds);
  609. this.paramsOption.connectorActIds = list;
  610. }
  611. },
  612. changeKeyType() {
  613. if (this.operationForm.keyType == 'PREDEFINED') {
  614. this.operationForm.confKey = '';
  615. } else {
  616. this.operationForm.customConfKey = '';
  617. }
  618. this.$refs['operationForm'].clearValidate()
  619. },
  620. changeFilterType() {
  621. if (this.operationForm.filterType == 'ChargingProfileId') {
  622. this.operationForm.connectorId = '';
  623. this.paramsOption.needConnector = false;
  624. this.paramsOption.needChargingProfile = true;
  625. this.operationForm.chargingProfilePurpose = undefined;
  626. this.paramsOption.needChargingProfilePurpose = false;
  627. } else {
  628. this.paramsOption.needConnector = true;
  629. this.operationForm.chargingProfilePk = '';
  630. this.paramsOption.needChargingProfile = false;
  631. this.paramsOption.needChargingProfilePurpose = true;
  632. if (this.paramsOption.connectorIds.length == 0) {
  633. this.changeRow();
  634. }
  635. }
  636. this.$refs['operationForm'].clearValidate()
  637. },
  638. getCheckedStation() {
  639. let list = [];
  640. this.stationList.forEach(item => {
  641. if (item.checked)
  642. list.push(item.stationId)
  643. })
  644. return list;
  645. },
  646. selectNone() {
  647. this.stationList.forEach(item => {
  648. item.checked = false;
  649. })
  650. this.paramsOption.connectorIds = [];
  651. this.operationForm.connectorId = '';
  652. this.operationForm.stationIds = [];
  653. },
  654. selectAll() {
  655. if (!this.operationInfo.singleStation) {
  656. this.stationList.forEach(item => {
  657. item.checked = true;
  658. })
  659. } else {
  660. this.stationList[0].checked = true;
  661. }
  662. this.changeRow();
  663. },
  664. handlePageChange() {
  665. this.changeSite()
  666. },
  667. perform() {
  668. this.$forceUpdate();
  669. this.$refs['operationForm'].validate(result => {
  670. if (result) {
  671. //this.addStation();
  672. if (this.operationForm.stationIds.length == 0) {
  673. this.$message({
  674. type: 'error',
  675. message: 'Please select at least 1 charge point'
  676. })
  677. } else if (this.operationInfo.api) {
  678. this.btnLoading = true;
  679. const params = Object.assign({}, this.operationForm)
  680. //对参数做特殊处理
  681. if (params.connectorId === NOT_SPECIFIC) {
  682. params.connectorId = ''
  683. }
  684. if (params.idTagInfo !== '') {
  685. params.idTagInfo = JSON.parse(params.idTagInfo)
  686. }
  687. ocpp.sendPerform(this.operationInfo.api, params).then(res => {
  688. this.btnLoading = false;
  689. if (res.data.taskId) {
  690. this.$router.push({path: '/ocpp-operations/result/' + res.data.taskId});
  691. }
  692. }).catch(err => {
  693. this.btnLoading = false;
  694. this.$message({
  695. type: 'error',
  696. message: err
  697. })
  698. });
  699. }
  700. }
  701. })
  702. },
  703. filterMobile(word) {
  704. if (word) {
  705. this.searchTag = word;
  706. setTimeout(() => {
  707. this.searchMobile(word)
  708. }, 300)
  709. } else {
  710. this.paramsOption.idTags = []
  711. this.paramsOption.idTags.push(...this.paramsOption.idTagAll)
  712. }
  713. },
  714. searchMobile(word) {
  715. console.log(this.searchTag, word);
  716. if (this.searchTag === word) {
  717. this.tagLoading = true;
  718. util.filterMobile(word, tags => {
  719. this.paramsOption.idTags = tags;
  720. this.tagLoading = false;
  721. })
  722. }
  723. }
  724. }
  725. }
  726. </script>
  727. <style scoped="scoped" lang='scss'>
  728. @import '../../styles/variables.scss';
  729. .card-container {
  730. width: 100%;
  731. padding: 20px 40px;
  732. min-height: $mainAppMinHeight;
  733. background-color: #F0F5FC;
  734. }
  735. .card-content {
  736. display: flex;
  737. overflow-x: auto;
  738. padding: 15px 5px;
  739. border-radius: 6px;
  740. flex-direction: row;
  741. flex-wrap: wrap-reverse;
  742. background-color: white;
  743. }
  744. .ocpp-content {
  745. flex: 1;
  746. min-width: 520px;
  747. padding: 0 35px 0 15px;
  748. }
  749. .section-title {
  750. color: #333;
  751. margin-top: 20px;
  752. margin-bottom: 30px;
  753. font-size: 16px;
  754. user-select: none;
  755. line-height: 24px;
  756. font-weight: 500;
  757. white-space: nowrap;
  758. font-family: sans-serif;
  759. text-transform: uppercase;
  760. }
  761. .perform {
  762. text-align: center;
  763. padding: 20px 80px 30px 0;
  764. }
  765. .multi-select {
  766. position: relative;
  767. min-height: 200px;
  768. }
  769. .note-span {
  770. top: 40px;
  771. left: 0px;
  772. width: 180px;
  773. color: #333333;
  774. padding: 6px 10px;
  775. font-size: 12px;
  776. border-radius: 3px;
  777. position: absolute;
  778. background-color: #56D905;
  779. }
  780. .flex-item {
  781. width: 100%;
  782. max-width: 320px;
  783. }
  784. @media screen and (max-width: 500px) {
  785. .card-container {
  786. padding: 0px;
  787. }
  788. .card-content {
  789. padding: 10px;
  790. }
  791. .ocpp-content {
  792. flex: 1;
  793. min-width: 320px;
  794. padding-right: 20px;
  795. }
  796. .perform {
  797. padding: 20px 0;
  798. }
  799. }
  800. </style>