@abi-software/map-side-bar 1.3.32 → 1.3.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +146 -146
- package/babel.config.js +14 -14
- package/dist/map-side-bar.common.js +226 -242
- package/dist/map-side-bar.common.js.map +1 -1
- package/dist/map-side-bar.css +1 -1
- package/dist/map-side-bar.umd.js +226 -242
- package/dist/map-side-bar.umd.js.map +1 -1
- package/dist/map-side-bar.umd.min.js +1 -1
- package/dist/map-side-bar.umd.min.js.map +1 -1
- package/package-lock.json +13716 -13716
- package/package.json +67 -67
- package/public/index.html +17 -17
- package/src/App.vue +152 -149
- package/src/algolia/algolia.js +182 -182
- package/src/algolia/utils.js +69 -69
- package/src/components/BadgesGroup.vue +142 -141
- package/src/components/Cascader.vue +49 -49
- package/src/components/ContextCard.vue +381 -381
- package/src/components/DatasetCard.vue +325 -325
- package/src/components/EventBus.js +3 -3
- package/src/components/ImageGallery.vue +515 -515
- package/src/components/SearchFilters.vue +609 -609
- package/src/components/SideBar.vue +224 -245
- package/src/components/SidebarContent.vue +508 -508
- package/src/components/Tabs.vue +78 -78
- package/src/components/hardcoded-context-info.js +79 -79
- package/src/components/index.js +8 -8
- package/src/components/species-map.js +8 -8
- package/src/main.js +8 -8
- package/static.json +6 -6
- package/vue.config.js +11 -11
- package/del.json +0 -27
- package/delte.json +0 -30
- package/scaffold_context_info.json +0 -31
- package/src/components/processFilters.js +0 -22
|
@@ -1,508 +1,508 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<el-card :body-style="bodyStyle" class="content-card">
|
|
3
|
-
<div slot="header" class="header">
|
|
4
|
-
<context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" :envVars="envVars"/>
|
|
5
|
-
<el-input
|
|
6
|
-
class="search-input"
|
|
7
|
-
placeholder="Search"
|
|
8
|
-
v-model="searchInput"
|
|
9
|
-
@keyup.native="searchEvent"
|
|
10
|
-
clearable
|
|
11
|
-
@clear="clearSearchClicked"
|
|
12
|
-
></el-input>
|
|
13
|
-
<el-button class="button" @click="searchEvent">Search</el-button>
|
|
14
|
-
</div>
|
|
15
|
-
<SearchFilters
|
|
16
|
-
class="filters"
|
|
17
|
-
ref="filtersRef"
|
|
18
|
-
:entry="filterEntry"
|
|
19
|
-
:envVars="envVars"
|
|
20
|
-
@filterResults="filterUpdate"
|
|
21
|
-
@numberPerPage="numberPerPageUpdate"
|
|
22
|
-
@loading="filtersLoading"
|
|
23
|
-
@cascaderReady="cascaderReady"
|
|
24
|
-
></SearchFilters>
|
|
25
|
-
<div class="content scrollbar" v-loading="loadingCards" ref="content">
|
|
26
|
-
<div
|
|
27
|
-
class="error-feedback"
|
|
28
|
-
v-if="results.length === 0 && !loadingCards"
|
|
29
|
-
>No results found - Please change your search / filter criteria.</div>
|
|
30
|
-
<div v-for="result in results" :key="result.doi" class="step-item">
|
|
31
|
-
<DatasetCard :entry="result" :envVars="envVars" @contextUpdate="contextCardUpdate"></DatasetCard>
|
|
32
|
-
</div>
|
|
33
|
-
<el-pagination
|
|
34
|
-
class="pagination"
|
|
35
|
-
:current-page.sync="page"
|
|
36
|
-
hide-on-single-page
|
|
37
|
-
large
|
|
38
|
-
layout="prev, pager, next"
|
|
39
|
-
:page-size="numberPerPage"
|
|
40
|
-
:total="numberOfHits"
|
|
41
|
-
@current-change="pageChange"
|
|
42
|
-
></el-pagination>
|
|
43
|
-
</div>
|
|
44
|
-
</el-card>
|
|
45
|
-
</template>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<script>
|
|
49
|
-
/* eslint-disable no-alert, no-console */
|
|
50
|
-
import Vue from "vue";
|
|
51
|
-
import {
|
|
52
|
-
Button,
|
|
53
|
-
Card,
|
|
54
|
-
Drawer,
|
|
55
|
-
Icon,
|
|
56
|
-
Input,
|
|
57
|
-
Loading,
|
|
58
|
-
Pagination
|
|
59
|
-
} from "element-ui";
|
|
60
|
-
import lang from "element-ui/lib/locale/lang/en";
|
|
61
|
-
import locale from "element-ui/lib/locale";
|
|
62
|
-
import SearchFilters from "./SearchFilters";
|
|
63
|
-
import DatasetCard from "./DatasetCard";
|
|
64
|
-
import ContextCard from "./ContextCard.vue";
|
|
65
|
-
import EventBus from "./EventBus"
|
|
66
|
-
|
|
67
|
-
import {AlgoliaClient} from "../algolia/algolia.js";
|
|
68
|
-
import {getFilters} from "../algolia/utils.js"
|
|
69
|
-
|
|
70
|
-
locale.use(lang);
|
|
71
|
-
Vue.use(Button);
|
|
72
|
-
Vue.use(Card);
|
|
73
|
-
Vue.use(Drawer);
|
|
74
|
-
Vue.use(Icon);
|
|
75
|
-
Vue.use(Input);
|
|
76
|
-
Vue.use(Loading);
|
|
77
|
-
Vue.use(Pagination);
|
|
78
|
-
|
|
79
|
-
// handleErrors: A custom fetch error handler to recieve messages from the server
|
|
80
|
-
// even when an error is found
|
|
81
|
-
var handleErrors = async function(response) {
|
|
82
|
-
if (!response.ok) {
|
|
83
|
-
let parse = await response.json();
|
|
84
|
-
if (parse) {
|
|
85
|
-
throw new Error(parse.message);
|
|
86
|
-
} else {
|
|
87
|
-
throw new Error(response);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return response;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
var initial_state = {
|
|
94
|
-
searchInput: "",
|
|
95
|
-
lastSearch: "",
|
|
96
|
-
results: [],
|
|
97
|
-
numberOfHits: 0,
|
|
98
|
-
filter: [],
|
|
99
|
-
loadingCards: false,
|
|
100
|
-
numberPerPage: 10,
|
|
101
|
-
page: 1,
|
|
102
|
-
pageModel: 1,
|
|
103
|
-
start: 0,
|
|
104
|
-
hasSearched: false,
|
|
105
|
-
contextCardEntry: undefined,
|
|
106
|
-
contextCardEnabled: true,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export default {
|
|
110
|
-
components: { SearchFilters, DatasetCard, ContextCard },
|
|
111
|
-
name: "SideBarContent",
|
|
112
|
-
props: {
|
|
113
|
-
visible: {
|
|
114
|
-
type: Boolean,
|
|
115
|
-
default: false
|
|
116
|
-
},
|
|
117
|
-
isDrawer: {
|
|
118
|
-
type: Boolean,
|
|
119
|
-
default: true
|
|
120
|
-
},
|
|
121
|
-
entry: {
|
|
122
|
-
type: Object,
|
|
123
|
-
default: () => initial_state
|
|
124
|
-
},
|
|
125
|
-
envVars: {
|
|
126
|
-
type: Object,
|
|
127
|
-
default: () => {}
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
data: function() {
|
|
131
|
-
return {
|
|
132
|
-
...this.entry,
|
|
133
|
-
bodyStyle: {
|
|
134
|
-
flex: "1 1 auto",
|
|
135
|
-
"flex-flow": "column",
|
|
136
|
-
display: "flex"
|
|
137
|
-
},
|
|
138
|
-
cascaderIsReady: false,
|
|
139
|
-
};
|
|
140
|
-
},
|
|
141
|
-
computed: {
|
|
142
|
-
// This computed property populates filter data's entry object with $data from this sidebar
|
|
143
|
-
filterEntry: function() {
|
|
144
|
-
return {
|
|
145
|
-
numberOfHits: this.numberOfHits,
|
|
146
|
-
filterFacets: this.filter
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
methods: {
|
|
151
|
-
contextCardUpdate: function(val){
|
|
152
|
-
this.contextCardEntry = val
|
|
153
|
-
},
|
|
154
|
-
resetSearch: function() {
|
|
155
|
-
this.numberOfHits = 0
|
|
156
|
-
this.discoverIds = []
|
|
157
|
-
this._dois = []
|
|
158
|
-
this.results = []
|
|
159
|
-
this.loadingCards = false
|
|
160
|
-
},
|
|
161
|
-
openSearch: function(filter, search='') {
|
|
162
|
-
this.searchInput = search;
|
|
163
|
-
this.resetPageNavigation();
|
|
164
|
-
//Proceed normally if cascader is ready
|
|
165
|
-
if (this.cascaderIsReady) {
|
|
166
|
-
this.filter = this.$refs.filtersRef.getValidatedFilters(filter);
|
|
167
|
-
//Facets provided but cannot find at least one valid
|
|
168
|
-
//facet. Tell the users the search is invalid and reset
|
|
169
|
-
//facets check boxes.
|
|
170
|
-
if ((filter && filter.length > 0) &&
|
|
171
|
-
(this.filter && this.filter.length === 0)) {
|
|
172
|
-
this.$refs.filtersRef.checkShowAllBoxes();
|
|
173
|
-
this.resetSearch();
|
|
174
|
-
} else if (this.filter) {
|
|
175
|
-
this.searchAlgolia(this.filter, search);
|
|
176
|
-
this.$refs.filtersRef.setCascader(this.filter);
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
//cascader is not ready, perform search if no filter is set,
|
|
180
|
-
//otherwise waith for cascader to be ready
|
|
181
|
-
this.filter = filter;
|
|
182
|
-
if (!filter || filter.length == 0) {
|
|
183
|
-
this.searchAlgolia(this.filter, search);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
addFilter: function(filter) {
|
|
188
|
-
if (this.cascaderIsReady) {
|
|
189
|
-
this.resetPageNavigation();
|
|
190
|
-
if (filter) {
|
|
191
|
-
if (this.$refs.filtersRef.addFilter(filter))
|
|
192
|
-
this.$refs.filtersRef.initiateSearch();
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
if (Array.isArray(this.filter)) {
|
|
196
|
-
this.filter.push(filter);
|
|
197
|
-
} else {
|
|
198
|
-
this.filter = [filter];
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
cascaderReady: function() {
|
|
203
|
-
this.cascaderIsReady = true;
|
|
204
|
-
this.openSearch(this.filter, this.searchInput);
|
|
205
|
-
},
|
|
206
|
-
clearSearchClicked: function() {
|
|
207
|
-
this.searchInput = "";
|
|
208
|
-
this.resetPageNavigation();
|
|
209
|
-
this.searchAlgolia(this.filters, this.searchInput);
|
|
210
|
-
},
|
|
211
|
-
searchEvent: function(event = false) {
|
|
212
|
-
if (event.keyCode === 13 || event instanceof MouseEvent) {
|
|
213
|
-
this.resetPageNavigation();
|
|
214
|
-
this.searchAlgolia(this.filters, this.searchInput);
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
filterUpdate: function(filters) {
|
|
218
|
-
this.filters = [...filters]
|
|
219
|
-
this.resetPageNavigation()
|
|
220
|
-
this.searchAlgolia(filters, this.searchInput)
|
|
221
|
-
this.$emit("search-changed", {
|
|
222
|
-
value: filters,
|
|
223
|
-
type: "filter-update"
|
|
224
|
-
});
|
|
225
|
-
},
|
|
226
|
-
searchAlgolia(filters, query=''){
|
|
227
|
-
// Algolia search
|
|
228
|
-
this.loadingCards = true
|
|
229
|
-
this.algoliaClient.anatomyInSearch(getFilters(filters), query).then(anatomy => {
|
|
230
|
-
EventBus.$emit("anatomyFound", anatomy)
|
|
231
|
-
})
|
|
232
|
-
this.algoliaClient.search(getFilters(filters), query, this.numberPerPage, this.page).then(searchData => {
|
|
233
|
-
this.numberOfHits = searchData.total
|
|
234
|
-
this.discoverIds = searchData.discoverIds
|
|
235
|
-
this._dois = searchData.dois
|
|
236
|
-
this.results = searchData.items
|
|
237
|
-
this.loadingCards = false
|
|
238
|
-
this.scrollToTop()
|
|
239
|
-
this.$emit("search-changed", { value: this.searchInput, type: "query-update" })
|
|
240
|
-
if (this._abortController)
|
|
241
|
-
this._abortController.abort()
|
|
242
|
-
this._abortController = new AbortController()
|
|
243
|
-
const signal = this._abortController.signal
|
|
244
|
-
//Search ongoing, let the current flow progress
|
|
245
|
-
this.perItemSearch(signal, { count: 0 })
|
|
246
|
-
})
|
|
247
|
-
},
|
|
248
|
-
filtersLoading: function (val) {
|
|
249
|
-
this.loadingCards = val;
|
|
250
|
-
},
|
|
251
|
-
numberPerPageUpdate: function(val) {
|
|
252
|
-
this.numberPerPage = val;
|
|
253
|
-
this.pageChange(1);
|
|
254
|
-
},
|
|
255
|
-
pageChange: function(page) {
|
|
256
|
-
this.start = (page - 1) * this.numberPerPage;
|
|
257
|
-
this.page = page
|
|
258
|
-
this.searchAlgolia(this.filters, this.searchInput, this.numberPerPage, this.page)
|
|
259
|
-
},
|
|
260
|
-
handleMissingData: function(doi) {
|
|
261
|
-
let i = this.results.findIndex(res=> res.doi === doi)
|
|
262
|
-
if (this.results[i])
|
|
263
|
-
this.results[i].detailsReady = true;
|
|
264
|
-
},
|
|
265
|
-
perItemSearch: function(signal, data) {
|
|
266
|
-
//Maximum 10 downloads at once to prevent long waiting time
|
|
267
|
-
//between unfinished search and new search
|
|
268
|
-
const maxDownloads = 10;
|
|
269
|
-
if (maxDownloads > data.count) {
|
|
270
|
-
const doi = this._dois.shift();
|
|
271
|
-
if (doi) {
|
|
272
|
-
data.count++;
|
|
273
|
-
this.callSciCrunch(this.envVars.API_LOCATION, {'dois': [doi]}, signal)
|
|
274
|
-
.then(result => {
|
|
275
|
-
if (result.numberOfHits === 0)
|
|
276
|
-
this.handleMissingData(doi);
|
|
277
|
-
else
|
|
278
|
-
this.resultsProcessing(result);
|
|
279
|
-
this.$refs.content.style["overflow-y"] = "scroll";
|
|
280
|
-
data.count--;
|
|
281
|
-
//Async::Download finished, get the next one
|
|
282
|
-
this.perItemSearch(signal, data);
|
|
283
|
-
})
|
|
284
|
-
.catch(result => {
|
|
285
|
-
if (result.name !== 'AbortError') {
|
|
286
|
-
this.handleMissingData(doi);
|
|
287
|
-
data.count--;
|
|
288
|
-
//Async::Download not aborted, get the next one
|
|
289
|
-
this.perItemSearch(signal, data);
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
//Check and make another request until it gets to max downloads
|
|
293
|
-
this.perItemSearch(signal, data);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
scrollToTop: function() {
|
|
298
|
-
if (this.$refs.content) {
|
|
299
|
-
this.$refs.content.scroll({ top: 0, behavior: "smooth" });
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
resetPageNavigation: function() {
|
|
303
|
-
this.start = 0;
|
|
304
|
-
this.page = 1;
|
|
305
|
-
},
|
|
306
|
-
resultsProcessing: function(data) {
|
|
307
|
-
this.lastSearch = this.searchInput;
|
|
308
|
-
|
|
309
|
-
if (data.results.length === 0) {
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
data.results.forEach(element => {
|
|
313
|
-
// match the scicrunch result with algolia result
|
|
314
|
-
let i = this.results.findIndex(res => element.doi ? element.doi.includes(res.doi) : false )
|
|
315
|
-
// Assign scicrunch results to the object
|
|
316
|
-
Object.assign(this.results[i], element)
|
|
317
|
-
// Assign the attributes that need some processing
|
|
318
|
-
Object.assign(this.results[i],{
|
|
319
|
-
numberSamples: element.sampleSize
|
|
320
|
-
? parseInt(element.sampleSize)
|
|
321
|
-
: 0,
|
|
322
|
-
numberSubjects: element.subjectSize
|
|
323
|
-
? parseInt(element.subjectSize)
|
|
324
|
-
: 0,
|
|
325
|
-
updated: (element.updated && element.updated.length) > 0 ? element.updated[0].timestamp.split("T")[0] : "",
|
|
326
|
-
url: element.uri[0],
|
|
327
|
-
datasetId: element.dataset_identifier,
|
|
328
|
-
datasetRevision: element.dataset_revision,
|
|
329
|
-
datasetVersion: element.dataset_version,
|
|
330
|
-
organs: (element.organs && element.organs.length > 0)
|
|
331
|
-
? [...new Set(element.organs.map(v => v.name))]
|
|
332
|
-
: undefined,
|
|
333
|
-
species: element.organisms
|
|
334
|
-
? element.organisms[0].species
|
|
335
|
-
? [...new Set(element.organisms.map((v) =>v.species ? v.species.name : null))]
|
|
336
|
-
: undefined
|
|
337
|
-
: undefined, // This processing only includes each gender once into 'sexes'
|
|
338
|
-
scaffolds: element['abi-scaffold-metadata-file'],
|
|
339
|
-
thumbnails: element['abi-thumbnail'] ? element['abi-thumbnail']: element['abi-scaffold-thumbnail'],
|
|
340
|
-
scaffoldViews: element['abi-scaffold-view-file'],
|
|
341
|
-
videos: element.video,
|
|
342
|
-
plots: element['abi-plot'],
|
|
343
|
-
images: element['common-images'],
|
|
344
|
-
contextualInformation: element['abi-contextual-information'].length > 0 ? element['abi-contextual-information'] : undefined,
|
|
345
|
-
segmentation: element['mbf-segmentation'],
|
|
346
|
-
simulation: element['abi-simulation-file'],
|
|
347
|
-
additionalLinks: element.additionalLinks,
|
|
348
|
-
detailsReady: true,
|
|
349
|
-
})
|
|
350
|
-
Vue.set(this.results, i, this.results[i])
|
|
351
|
-
});
|
|
352
|
-
},
|
|
353
|
-
createfilterParams: function(params) {
|
|
354
|
-
let p = new URLSearchParams();
|
|
355
|
-
//Check if field is array or value
|
|
356
|
-
for (const key in params) {
|
|
357
|
-
if (Array.isArray(params[key])) {
|
|
358
|
-
params[key].forEach(e => {
|
|
359
|
-
p.append(key, e);
|
|
360
|
-
});
|
|
361
|
-
} else {
|
|
362
|
-
p.append(key, params[key]);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return p.toString();
|
|
366
|
-
},
|
|
367
|
-
callSciCrunch: function(apiLocation, params = {}, signal) {
|
|
368
|
-
return new Promise((resolve, reject) => {
|
|
369
|
-
// Add parameters if we are sent them
|
|
370
|
-
let fullEndpoint = this.envVars.API_LOCATION + this.searchEndpoint + "?" + this.createfilterParams(params);
|
|
371
|
-
fetch(fullEndpoint, {signal})
|
|
372
|
-
.then(handleErrors)
|
|
373
|
-
.then(response => response.json())
|
|
374
|
-
.then(data => resolve(data))
|
|
375
|
-
.catch(data => reject(data));
|
|
376
|
-
});
|
|
377
|
-
},
|
|
378
|
-
},
|
|
379
|
-
mounted: function() {
|
|
380
|
-
// initialise algolia
|
|
381
|
-
this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
|
|
382
|
-
this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
|
|
383
|
-
this.openSearch(
|
|
384
|
-
},
|
|
385
|
-
created: function() {
|
|
386
|
-
//Create non-reactive local variables
|
|
387
|
-
this.searchEndpoint = "dataset_info/using_multiple_dois/";
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
</script>
|
|
391
|
-
|
|
392
|
-
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
393
|
-
<style scoped>
|
|
394
|
-
.content-card {
|
|
395
|
-
height: 100%;
|
|
396
|
-
flex-flow: column;
|
|
397
|
-
display: flex;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.button {
|
|
401
|
-
background-color: #8300bf;
|
|
402
|
-
border: #8300bf;
|
|
403
|
-
color: white;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
.step-item {
|
|
407
|
-
font-size: 14px;
|
|
408
|
-
margin-bottom: 18px;
|
|
409
|
-
text-align: left;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
.search-input {
|
|
413
|
-
width: 298px !important;
|
|
414
|
-
height: 40px;
|
|
415
|
-
padding-right: 14px;
|
|
416
|
-
align-items: left;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.header {
|
|
420
|
-
border: solid 1px #292b66;
|
|
421
|
-
background-color: #292b66;
|
|
422
|
-
text-align: left;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
.pagination {
|
|
426
|
-
padding-bottom: 16px;
|
|
427
|
-
background-color: white;
|
|
428
|
-
text-align: center;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
.pagination >>> button {
|
|
432
|
-
background-color: white !important;
|
|
433
|
-
}
|
|
434
|
-
.pagination >>> li {
|
|
435
|
-
background-color: white !important;
|
|
436
|
-
}
|
|
437
|
-
.pagination >>> li.active {
|
|
438
|
-
color: #8300bf;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
.error-feedback {
|
|
442
|
-
font-family: Asap;
|
|
443
|
-
font-size: 14px;
|
|
444
|
-
font-style: italic;
|
|
445
|
-
padding-top: 15px;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
.content-card >>> .el-card__header {
|
|
449
|
-
background-color: #292b66;
|
|
450
|
-
border: solid 1px #292b66;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
.content-card >>> .el-card__body {
|
|
454
|
-
background-color: #f7faff;
|
|
455
|
-
overflow-y: hidden;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
.content {
|
|
459
|
-
width: 518px;
|
|
460
|
-
flex: 1 1 auto;
|
|
461
|
-
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
|
|
462
|
-
border: solid 1px #e4e7ed;
|
|
463
|
-
background-color: #ffffff;
|
|
464
|
-
overflow-y: scroll;
|
|
465
|
-
scrollbar-width: thin;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
.content >>> .el-loading-spinner .path {
|
|
469
|
-
stroke: #8300bf;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.content >>> .step-item:first-child .seperator-path{
|
|
473
|
-
display: none;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
.content >>> .step-item:not(:first-child) .seperator-path{
|
|
477
|
-
width: 486px;
|
|
478
|
-
height: 0px;
|
|
479
|
-
border: solid 1px #e4e7ed;
|
|
480
|
-
background-color: #e4e7ed;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
.scrollbar::-webkit-scrollbar-track {
|
|
484
|
-
border-radius: 10px;
|
|
485
|
-
background-color: #f5f5f5;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
.scrollbar::-webkit-scrollbar {
|
|
489
|
-
width: 12px;
|
|
490
|
-
right: -12px;
|
|
491
|
-
background-color: #f5f5f5;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
.scrollbar::-webkit-scrollbar-thumb {
|
|
495
|
-
border-radius: 4px;
|
|
496
|
-
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
|
497
|
-
background-color: #979797;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
>>> .el-input__suffix {
|
|
501
|
-
padding-right: 10px;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
>>> .my-drawer {
|
|
505
|
-
background: rgba(0, 0, 0, 0);
|
|
506
|
-
box-shadow: none;
|
|
507
|
-
}
|
|
508
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<el-card :body-style="bodyStyle" class="content-card">
|
|
3
|
+
<div slot="header" class="header">
|
|
4
|
+
<context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" :envVars="envVars"/>
|
|
5
|
+
<el-input
|
|
6
|
+
class="search-input"
|
|
7
|
+
placeholder="Search"
|
|
8
|
+
v-model="searchInput"
|
|
9
|
+
@keyup.native="searchEvent"
|
|
10
|
+
clearable
|
|
11
|
+
@clear="clearSearchClicked"
|
|
12
|
+
></el-input>
|
|
13
|
+
<el-button class="button" @click="searchEvent">Search</el-button>
|
|
14
|
+
</div>
|
|
15
|
+
<SearchFilters
|
|
16
|
+
class="filters"
|
|
17
|
+
ref="filtersRef"
|
|
18
|
+
:entry="filterEntry"
|
|
19
|
+
:envVars="envVars"
|
|
20
|
+
@filterResults="filterUpdate"
|
|
21
|
+
@numberPerPage="numberPerPageUpdate"
|
|
22
|
+
@loading="filtersLoading"
|
|
23
|
+
@cascaderReady="cascaderReady"
|
|
24
|
+
></SearchFilters>
|
|
25
|
+
<div class="content scrollbar" v-loading="loadingCards" ref="content">
|
|
26
|
+
<div
|
|
27
|
+
class="error-feedback"
|
|
28
|
+
v-if="results.length === 0 && !loadingCards"
|
|
29
|
+
>No results found - Please change your search / filter criteria.</div>
|
|
30
|
+
<div v-for="result in results" :key="result.doi" class="step-item">
|
|
31
|
+
<DatasetCard :entry="result" :envVars="envVars" @contextUpdate="contextCardUpdate"></DatasetCard>
|
|
32
|
+
</div>
|
|
33
|
+
<el-pagination
|
|
34
|
+
class="pagination"
|
|
35
|
+
:current-page.sync="page"
|
|
36
|
+
hide-on-single-page
|
|
37
|
+
large
|
|
38
|
+
layout="prev, pager, next"
|
|
39
|
+
:page-size="numberPerPage"
|
|
40
|
+
:total="numberOfHits"
|
|
41
|
+
@current-change="pageChange"
|
|
42
|
+
></el-pagination>
|
|
43
|
+
</div>
|
|
44
|
+
</el-card>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
<script>
|
|
49
|
+
/* eslint-disable no-alert, no-console */
|
|
50
|
+
import Vue from "vue";
|
|
51
|
+
import {
|
|
52
|
+
Button,
|
|
53
|
+
Card,
|
|
54
|
+
Drawer,
|
|
55
|
+
Icon,
|
|
56
|
+
Input,
|
|
57
|
+
Loading,
|
|
58
|
+
Pagination
|
|
59
|
+
} from "element-ui";
|
|
60
|
+
import lang from "element-ui/lib/locale/lang/en";
|
|
61
|
+
import locale from "element-ui/lib/locale";
|
|
62
|
+
import SearchFilters from "./SearchFilters";
|
|
63
|
+
import DatasetCard from "./DatasetCard";
|
|
64
|
+
import ContextCard from "./ContextCard.vue";
|
|
65
|
+
import EventBus from "./EventBus"
|
|
66
|
+
|
|
67
|
+
import {AlgoliaClient} from "../algolia/algolia.js";
|
|
68
|
+
import {getFilters} from "../algolia/utils.js"
|
|
69
|
+
|
|
70
|
+
locale.use(lang);
|
|
71
|
+
Vue.use(Button);
|
|
72
|
+
Vue.use(Card);
|
|
73
|
+
Vue.use(Drawer);
|
|
74
|
+
Vue.use(Icon);
|
|
75
|
+
Vue.use(Input);
|
|
76
|
+
Vue.use(Loading);
|
|
77
|
+
Vue.use(Pagination);
|
|
78
|
+
|
|
79
|
+
// handleErrors: A custom fetch error handler to recieve messages from the server
|
|
80
|
+
// even when an error is found
|
|
81
|
+
var handleErrors = async function(response) {
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
let parse = await response.json();
|
|
84
|
+
if (parse) {
|
|
85
|
+
throw new Error(parse.message);
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error(response);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return response;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
var initial_state = {
|
|
94
|
+
searchInput: "",
|
|
95
|
+
lastSearch: "",
|
|
96
|
+
results: [],
|
|
97
|
+
numberOfHits: 0,
|
|
98
|
+
filter: [],
|
|
99
|
+
loadingCards: false,
|
|
100
|
+
numberPerPage: 10,
|
|
101
|
+
page: 1,
|
|
102
|
+
pageModel: 1,
|
|
103
|
+
start: 0,
|
|
104
|
+
hasSearched: false,
|
|
105
|
+
contextCardEntry: undefined,
|
|
106
|
+
contextCardEnabled: true,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default {
|
|
110
|
+
components: { SearchFilters, DatasetCard, ContextCard },
|
|
111
|
+
name: "SideBarContent",
|
|
112
|
+
props: {
|
|
113
|
+
visible: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: false
|
|
116
|
+
},
|
|
117
|
+
isDrawer: {
|
|
118
|
+
type: Boolean,
|
|
119
|
+
default: true
|
|
120
|
+
},
|
|
121
|
+
entry: {
|
|
122
|
+
type: Object,
|
|
123
|
+
default: () => initial_state
|
|
124
|
+
},
|
|
125
|
+
envVars: {
|
|
126
|
+
type: Object,
|
|
127
|
+
default: () => {}
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
data: function() {
|
|
131
|
+
return {
|
|
132
|
+
...this.entry,
|
|
133
|
+
bodyStyle: {
|
|
134
|
+
flex: "1 1 auto",
|
|
135
|
+
"flex-flow": "column",
|
|
136
|
+
display: "flex"
|
|
137
|
+
},
|
|
138
|
+
cascaderIsReady: false,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
computed: {
|
|
142
|
+
// This computed property populates filter data's entry object with $data from this sidebar
|
|
143
|
+
filterEntry: function() {
|
|
144
|
+
return {
|
|
145
|
+
numberOfHits: this.numberOfHits,
|
|
146
|
+
filterFacets: this.filter
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
methods: {
|
|
151
|
+
contextCardUpdate: function(val){
|
|
152
|
+
this.contextCardEntry = val
|
|
153
|
+
},
|
|
154
|
+
resetSearch: function() {
|
|
155
|
+
this.numberOfHits = 0
|
|
156
|
+
this.discoverIds = []
|
|
157
|
+
this._dois = []
|
|
158
|
+
this.results = []
|
|
159
|
+
this.loadingCards = false
|
|
160
|
+
},
|
|
161
|
+
openSearch: function(filter, search='') {
|
|
162
|
+
this.searchInput = search;
|
|
163
|
+
this.resetPageNavigation();
|
|
164
|
+
//Proceed normally if cascader is ready
|
|
165
|
+
if (this.cascaderIsReady) {
|
|
166
|
+
this.filter = this.$refs.filtersRef.getValidatedFilters(filter);
|
|
167
|
+
//Facets provided but cannot find at least one valid
|
|
168
|
+
//facet. Tell the users the search is invalid and reset
|
|
169
|
+
//facets check boxes.
|
|
170
|
+
if ((filter && filter.length > 0) &&
|
|
171
|
+
(this.filter && this.filter.length === 0)) {
|
|
172
|
+
this.$refs.filtersRef.checkShowAllBoxes();
|
|
173
|
+
this.resetSearch();
|
|
174
|
+
} else if (this.filter) {
|
|
175
|
+
this.searchAlgolia(this.filter, search);
|
|
176
|
+
this.$refs.filtersRef.setCascader(this.filter);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
//cascader is not ready, perform search if no filter is set,
|
|
180
|
+
//otherwise waith for cascader to be ready
|
|
181
|
+
this.filter = filter;
|
|
182
|
+
if (!filter || filter.length == 0) {
|
|
183
|
+
this.searchAlgolia(this.filter, search);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
addFilter: function(filter) {
|
|
188
|
+
if (this.cascaderIsReady) {
|
|
189
|
+
this.resetPageNavigation();
|
|
190
|
+
if (filter) {
|
|
191
|
+
if (this.$refs.filtersRef.addFilter(filter))
|
|
192
|
+
this.$refs.filtersRef.initiateSearch();
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
if (Array.isArray(this.filter)) {
|
|
196
|
+
this.filter.push(filter);
|
|
197
|
+
} else {
|
|
198
|
+
this.filter = [filter];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
cascaderReady: function() {
|
|
203
|
+
this.cascaderIsReady = true;
|
|
204
|
+
this.openSearch(this.filter, this.searchInput);
|
|
205
|
+
},
|
|
206
|
+
clearSearchClicked: function() {
|
|
207
|
+
this.searchInput = "";
|
|
208
|
+
this.resetPageNavigation();
|
|
209
|
+
this.searchAlgolia(this.filters, this.searchInput);
|
|
210
|
+
},
|
|
211
|
+
searchEvent: function(event = false) {
|
|
212
|
+
if (event.keyCode === 13 || event instanceof MouseEvent) {
|
|
213
|
+
this.resetPageNavigation();
|
|
214
|
+
this.searchAlgolia(this.filters, this.searchInput);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
filterUpdate: function(filters) {
|
|
218
|
+
this.filters = [...filters]
|
|
219
|
+
this.resetPageNavigation()
|
|
220
|
+
this.searchAlgolia(filters, this.searchInput)
|
|
221
|
+
this.$emit("search-changed", {
|
|
222
|
+
value: filters,
|
|
223
|
+
type: "filter-update"
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
searchAlgolia(filters, query=''){
|
|
227
|
+
// Algolia search
|
|
228
|
+
this.loadingCards = true
|
|
229
|
+
this.algoliaClient.anatomyInSearch(getFilters(filters), query).then(anatomy => {
|
|
230
|
+
EventBus.$emit("anatomyFound", anatomy)
|
|
231
|
+
})
|
|
232
|
+
this.algoliaClient.search(getFilters(filters), query, this.numberPerPage, this.page).then(searchData => {
|
|
233
|
+
this.numberOfHits = searchData.total
|
|
234
|
+
this.discoverIds = searchData.discoverIds
|
|
235
|
+
this._dois = searchData.dois
|
|
236
|
+
this.results = searchData.items
|
|
237
|
+
this.loadingCards = false
|
|
238
|
+
this.scrollToTop()
|
|
239
|
+
this.$emit("search-changed", { value: this.searchInput, type: "query-update" })
|
|
240
|
+
if (this._abortController)
|
|
241
|
+
this._abortController.abort()
|
|
242
|
+
this._abortController = new AbortController()
|
|
243
|
+
const signal = this._abortController.signal
|
|
244
|
+
//Search ongoing, let the current flow progress
|
|
245
|
+
this.perItemSearch(signal, { count: 0 })
|
|
246
|
+
})
|
|
247
|
+
},
|
|
248
|
+
filtersLoading: function (val) {
|
|
249
|
+
this.loadingCards = val;
|
|
250
|
+
},
|
|
251
|
+
numberPerPageUpdate: function(val) {
|
|
252
|
+
this.numberPerPage = val;
|
|
253
|
+
this.pageChange(1);
|
|
254
|
+
},
|
|
255
|
+
pageChange: function(page) {
|
|
256
|
+
this.start = (page - 1) * this.numberPerPage;
|
|
257
|
+
this.page = page
|
|
258
|
+
this.searchAlgolia(this.filters, this.searchInput, this.numberPerPage, this.page)
|
|
259
|
+
},
|
|
260
|
+
handleMissingData: function(doi) {
|
|
261
|
+
let i = this.results.findIndex(res=> res.doi === doi)
|
|
262
|
+
if (this.results[i])
|
|
263
|
+
this.results[i].detailsReady = true;
|
|
264
|
+
},
|
|
265
|
+
perItemSearch: function(signal, data) {
|
|
266
|
+
//Maximum 10 downloads at once to prevent long waiting time
|
|
267
|
+
//between unfinished search and new search
|
|
268
|
+
const maxDownloads = 10;
|
|
269
|
+
if (maxDownloads > data.count) {
|
|
270
|
+
const doi = this._dois.shift();
|
|
271
|
+
if (doi) {
|
|
272
|
+
data.count++;
|
|
273
|
+
this.callSciCrunch(this.envVars.API_LOCATION, {'dois': [doi]}, signal)
|
|
274
|
+
.then(result => {
|
|
275
|
+
if (result.numberOfHits === 0)
|
|
276
|
+
this.handleMissingData(doi);
|
|
277
|
+
else
|
|
278
|
+
this.resultsProcessing(result);
|
|
279
|
+
this.$refs.content.style["overflow-y"] = "scroll";
|
|
280
|
+
data.count--;
|
|
281
|
+
//Async::Download finished, get the next one
|
|
282
|
+
this.perItemSearch(signal, data);
|
|
283
|
+
})
|
|
284
|
+
.catch(result => {
|
|
285
|
+
if (result.name !== 'AbortError') {
|
|
286
|
+
this.handleMissingData(doi);
|
|
287
|
+
data.count--;
|
|
288
|
+
//Async::Download not aborted, get the next one
|
|
289
|
+
this.perItemSearch(signal, data);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
//Check and make another request until it gets to max downloads
|
|
293
|
+
this.perItemSearch(signal, data);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
scrollToTop: function() {
|
|
298
|
+
if (this.$refs.content) {
|
|
299
|
+
this.$refs.content.scroll({ top: 0, behavior: "smooth" });
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
resetPageNavigation: function() {
|
|
303
|
+
this.start = 0;
|
|
304
|
+
this.page = 1;
|
|
305
|
+
},
|
|
306
|
+
resultsProcessing: function(data) {
|
|
307
|
+
this.lastSearch = this.searchInput;
|
|
308
|
+
|
|
309
|
+
if (data.results.length === 0) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
data.results.forEach(element => {
|
|
313
|
+
// match the scicrunch result with algolia result
|
|
314
|
+
let i = this.results.findIndex(res => element.doi ? element.doi.includes(res.doi) : false )
|
|
315
|
+
// Assign scicrunch results to the object
|
|
316
|
+
Object.assign(this.results[i], element)
|
|
317
|
+
// Assign the attributes that need some processing
|
|
318
|
+
Object.assign(this.results[i],{
|
|
319
|
+
numberSamples: element.sampleSize
|
|
320
|
+
? parseInt(element.sampleSize)
|
|
321
|
+
: 0,
|
|
322
|
+
numberSubjects: element.subjectSize
|
|
323
|
+
? parseInt(element.subjectSize)
|
|
324
|
+
: 0,
|
|
325
|
+
updated: (element.updated && element.updated.length) > 0 ? element.updated[0].timestamp.split("T")[0] : "",
|
|
326
|
+
url: element.uri[0],
|
|
327
|
+
datasetId: element.dataset_identifier,
|
|
328
|
+
datasetRevision: element.dataset_revision,
|
|
329
|
+
datasetVersion: element.dataset_version,
|
|
330
|
+
organs: (element.organs && element.organs.length > 0)
|
|
331
|
+
? [...new Set(element.organs.map(v => v.name))]
|
|
332
|
+
: undefined,
|
|
333
|
+
species: element.organisms
|
|
334
|
+
? element.organisms[0].species
|
|
335
|
+
? [...new Set(element.organisms.map((v) =>v.species ? v.species.name : null))]
|
|
336
|
+
: undefined
|
|
337
|
+
: undefined, // This processing only includes each gender once into 'sexes'
|
|
338
|
+
scaffolds: element['abi-scaffold-metadata-file'],
|
|
339
|
+
thumbnails: element['abi-thumbnail'] ? element['abi-thumbnail']: element['abi-scaffold-thumbnail'],
|
|
340
|
+
scaffoldViews: element['abi-scaffold-view-file'],
|
|
341
|
+
videos: element.video,
|
|
342
|
+
plots: element['abi-plot'],
|
|
343
|
+
images: element['common-images'],
|
|
344
|
+
contextualInformation: element['abi-contextual-information'].length > 0 ? element['abi-contextual-information'] : undefined,
|
|
345
|
+
segmentation: element['mbf-segmentation'],
|
|
346
|
+
simulation: element['abi-simulation-file'],
|
|
347
|
+
additionalLinks: element.additionalLinks,
|
|
348
|
+
detailsReady: true,
|
|
349
|
+
})
|
|
350
|
+
Vue.set(this.results, i, this.results[i])
|
|
351
|
+
});
|
|
352
|
+
},
|
|
353
|
+
createfilterParams: function(params) {
|
|
354
|
+
let p = new URLSearchParams();
|
|
355
|
+
//Check if field is array or value
|
|
356
|
+
for (const key in params) {
|
|
357
|
+
if (Array.isArray(params[key])) {
|
|
358
|
+
params[key].forEach(e => {
|
|
359
|
+
p.append(key, e);
|
|
360
|
+
});
|
|
361
|
+
} else {
|
|
362
|
+
p.append(key, params[key]);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return p.toString();
|
|
366
|
+
},
|
|
367
|
+
callSciCrunch: function(apiLocation, params = {}, signal) {
|
|
368
|
+
return new Promise((resolve, reject) => {
|
|
369
|
+
// Add parameters if we are sent them
|
|
370
|
+
let fullEndpoint = this.envVars.API_LOCATION + this.searchEndpoint + "?" + this.createfilterParams(params);
|
|
371
|
+
fetch(fullEndpoint, {signal})
|
|
372
|
+
.then(handleErrors)
|
|
373
|
+
.then(response => response.json())
|
|
374
|
+
.then(data => resolve(data))
|
|
375
|
+
.catch(data => reject(data));
|
|
376
|
+
});
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
mounted: function() {
|
|
380
|
+
// initialise algolia
|
|
381
|
+
this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
|
|
382
|
+
this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
|
|
383
|
+
this.openSearch(this.filter, this.searchInput);
|
|
384
|
+
},
|
|
385
|
+
created: function() {
|
|
386
|
+
//Create non-reactive local variables
|
|
387
|
+
this.searchEndpoint = "dataset_info/using_multiple_dois/";
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
</script>
|
|
391
|
+
|
|
392
|
+
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
393
|
+
<style scoped>
|
|
394
|
+
.content-card {
|
|
395
|
+
height: 100%;
|
|
396
|
+
flex-flow: column;
|
|
397
|
+
display: flex;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.button {
|
|
401
|
+
background-color: #8300bf;
|
|
402
|
+
border: #8300bf;
|
|
403
|
+
color: white;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.step-item {
|
|
407
|
+
font-size: 14px;
|
|
408
|
+
margin-bottom: 18px;
|
|
409
|
+
text-align: left;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.search-input {
|
|
413
|
+
width: 298px !important;
|
|
414
|
+
height: 40px;
|
|
415
|
+
padding-right: 14px;
|
|
416
|
+
align-items: left;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.header {
|
|
420
|
+
border: solid 1px #292b66;
|
|
421
|
+
background-color: #292b66;
|
|
422
|
+
text-align: left;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.pagination {
|
|
426
|
+
padding-bottom: 16px;
|
|
427
|
+
background-color: white;
|
|
428
|
+
text-align: center;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.pagination >>> button {
|
|
432
|
+
background-color: white !important;
|
|
433
|
+
}
|
|
434
|
+
.pagination >>> li {
|
|
435
|
+
background-color: white !important;
|
|
436
|
+
}
|
|
437
|
+
.pagination >>> li.active {
|
|
438
|
+
color: #8300bf;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.error-feedback {
|
|
442
|
+
font-family: Asap;
|
|
443
|
+
font-size: 14px;
|
|
444
|
+
font-style: italic;
|
|
445
|
+
padding-top: 15px;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.content-card >>> .el-card__header {
|
|
449
|
+
background-color: #292b66;
|
|
450
|
+
border: solid 1px #292b66;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.content-card >>> .el-card__body {
|
|
454
|
+
background-color: #f7faff;
|
|
455
|
+
overflow-y: hidden;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.content {
|
|
459
|
+
width: 518px;
|
|
460
|
+
flex: 1 1 auto;
|
|
461
|
+
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
|
|
462
|
+
border: solid 1px #e4e7ed;
|
|
463
|
+
background-color: #ffffff;
|
|
464
|
+
overflow-y: scroll;
|
|
465
|
+
scrollbar-width: thin;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.content >>> .el-loading-spinner .path {
|
|
469
|
+
stroke: #8300bf;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.content >>> .step-item:first-child .seperator-path{
|
|
473
|
+
display: none;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.content >>> .step-item:not(:first-child) .seperator-path{
|
|
477
|
+
width: 486px;
|
|
478
|
+
height: 0px;
|
|
479
|
+
border: solid 1px #e4e7ed;
|
|
480
|
+
background-color: #e4e7ed;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.scrollbar::-webkit-scrollbar-track {
|
|
484
|
+
border-radius: 10px;
|
|
485
|
+
background-color: #f5f5f5;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.scrollbar::-webkit-scrollbar {
|
|
489
|
+
width: 12px;
|
|
490
|
+
right: -12px;
|
|
491
|
+
background-color: #f5f5f5;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.scrollbar::-webkit-scrollbar-thumb {
|
|
495
|
+
border-radius: 4px;
|
|
496
|
+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
|
497
|
+
background-color: #979797;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
>>> .el-input__suffix {
|
|
501
|
+
padding-right: 10px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
>>> .my-drawer {
|
|
505
|
+
background: rgba(0, 0, 0, 0);
|
|
506
|
+
box-shadow: none;
|
|
507
|
+
}
|
|
508
|
+
</style>
|