Implemented detail view proof-of-concept for photo search
This commit is contained in:
parent
d7e4531231
commit
4ac3b2a602
|
@ -75,6 +75,7 @@
|
|||
"vue-router": "^2.7.0",
|
||||
"vue-style-loader": "^2.0.0",
|
||||
"vue-template-compiler": "^2.4.4",
|
||||
"vue-truncate-filter": "^1.1.7",
|
||||
"vuelidate": "^0.4.3",
|
||||
"vuetify": "^1.2.3",
|
||||
"webpack": "^3.12.0",
|
||||
|
|
|
@ -12,6 +12,7 @@ import Session from 'common/session';
|
|||
import Event from 'pubsub-js';
|
||||
import Moment from 'vue-moment';
|
||||
import InfiniteScroll from 'vue-infinite-scroll';
|
||||
import VueTruncate from 'vue-truncate-filter';
|
||||
|
||||
const session = new Session(window.localStorage);
|
||||
const config = new Config(window.localStorage, window.appConfig);
|
||||
|
@ -37,6 +38,7 @@ Vue.use(Vuetify, {
|
|||
|
||||
Vue.use(Moment);
|
||||
Vue.use(InfiniteScroll);
|
||||
Vue.use(VueTruncate);
|
||||
Vue.use(AppComponents);
|
||||
Vue.use(Router);
|
||||
|
||||
|
|
|
@ -160,6 +160,59 @@
|
|||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<v-container grid-list-xs fluid class="pa-0" v-if="query.view === 'details'">
|
||||
<v-card v-if="results.length === 0">
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="headline mb-3">No photos matched your search</h3>
|
||||
<div>Try using other terms and search options such as category, country and camera.</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
<v-layout row wrap>
|
||||
<v-flex
|
||||
v-for="photo in results"
|
||||
:key="photo.ID"
|
||||
xs12 sm6 md4 lg3 d-flex
|
||||
>
|
||||
<v-card tile class="ma-2">
|
||||
<v-img
|
||||
:src="'/api/v1/files/' + photo.FileHash + '/square_thumbnail?size=500'"
|
||||
aspect-ratio="1"
|
||||
v-bind:class="{ selected: photo.selected }"
|
||||
@click="selectPhoto(photo)"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
</v-img>
|
||||
|
||||
<v-card-title primary-title class="pa-3">
|
||||
<div>
|
||||
<h3 class="subheading text-truncate mb-0">{{ photo.PhotoTitle | truncate(50)}}</h3>
|
||||
<div><v-icon small>date_range</v-icon> {{ photo.TakenAt | moment('DD/MM/YYYY hh:mm:ss') }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-icon small>photo_camera</v-icon> {{ photo.CameraModel }}<v-spacer></v-spacer>
|
||||
<v-icon small>location_on</v-icon> {{ photo.LocName ? photo.LocName + ', ' : ''}}{{ photo.LocCity ? photo.LocCity + ', ' : ''}}{{ photo.LocCounty ? photo.LocCounty + ', ' : ''}}{{ photo.LocCountry }}
|
||||
</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
||||
<!-- v-card-actions>
|
||||
<v-btn flat color="orange">Like</v-btn>
|
||||
<v-btn flat color="orange">Edit</v-btn>
|
||||
</v-card-actions -->
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
|
||||
|
||||
<v-container grid-list-xs fluid class="pa-0" v-if="query.view === 'tiles'">
|
||||
<v-card v-if="results.length === 0">
|
||||
|
@ -181,7 +234,7 @@
|
|||
<v-tooltip bottom>
|
||||
<v-card-actions flat tile class="d-flex" slot="activator" @click="selectPhoto(photo)"
|
||||
@mouseover="overPhoto(photo)" @mouseleave="leavePhoto(photo)">
|
||||
<v-img :src="'/api/v1/files/' + photo.FileID + '/square_thumbnail?size=500'"
|
||||
<v-img :src="'/api/v1/files/' + photo.FileHash + '/square_thumbnail?size=500'"
|
||||
aspect-ratio="1"
|
||||
class="grey lighten-2"
|
||||
>
|
||||
|
@ -233,7 +286,7 @@
|
|||
const camera = query['camera'] ? parseInt(query['camera']) : 0;
|
||||
const q = query['q'] ? query['q'] : '';
|
||||
const country = query['country'] ? query['country'] : '';
|
||||
const view = query['view'] === 'list' ? 'list' : 'tiles';
|
||||
const view = query['view'] ? query['view'] : 'tiles';
|
||||
const cameras = [{ID: 0, CameraModel: 'All Cameras'}].concat(this.$config.getValue('cameras'));
|
||||
const countries = [{
|
||||
LocCountryCode: '',
|
||||
|
@ -264,6 +317,7 @@
|
|||
],
|
||||
'views': [
|
||||
{value: 'tiles', text: 'Tiles'},
|
||||
{value: 'details', text: 'Details'},
|
||||
{value: 'list', text: 'List'},
|
||||
],
|
||||
'countries': countries,
|
||||
|
@ -275,12 +329,12 @@
|
|||
],
|
||||
},
|
||||
'listColumns': [
|
||||
{ text: 'Title', value: 'PhotoTitle' },
|
||||
{ text: 'Taken At', value: 'TakenAt' },
|
||||
{ text: 'City', value: 'LocCity' },
|
||||
{ text: 'Country', value: 'LocCountry' },
|
||||
{ text: 'Camera', value: 'CameraModel' },
|
||||
{ text: 'Favorite', value: 'PhotoFavorite' },
|
||||
{text: 'Title', value: 'PhotoTitle'},
|
||||
{text: 'Taken At', value: 'TakenAt'},
|
||||
{text: 'City', value: 'LocCity'},
|
||||
{text: 'Country', value: 'LocCountry'},
|
||||
{text: 'Camera', value: 'CameraModel'},
|
||||
{text: 'Favorite', value: 'PhotoFavorite'},
|
||||
],
|
||||
'view': view,
|
||||
'loadMoreDisabled': true,
|
||||
|
|
|
@ -6987,6 +6987,10 @@ vue-template-es2015-compiler@^1.2.2:
|
|||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||
|
||||
vue-truncate-filter@^1.1.7:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/vue-truncate-filter/-/vue-truncate-filter-1.1.7.tgz#e365fd2d4520c017293308e0ecbb1dd6176c3af2"
|
||||
|
||||
vue@^2.4.4:
|
||||
version "2.5.16"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||
|
|
|
@ -57,6 +57,7 @@ type PhotoSearchResult struct {
|
|||
// File
|
||||
FileID uint
|
||||
FileName string
|
||||
FileHash string
|
||||
FileType string
|
||||
FileMime string
|
||||
FileWidth int
|
||||
|
@ -81,7 +82,7 @@ func (s *Search) Photos(form PhotoSearchForm) ([]PhotoSearchResult, error) {
|
|||
q := s.db.NewScope(nil).DB()
|
||||
q = q.Table("photos").
|
||||
Select(`SQL_CALC_FOUND_ROWS photos.*,
|
||||
files.id AS file_id, files.file_name, files.file_type, files.file_mime, files.file_width, files.file_height, files.file_aspect_ratio, files.file_orientation,
|
||||
files.id AS file_id, files.file_name, files.file_hash, files.file_type, files.file_mime, files.file_width, files.file_height, files.file_aspect_ratio, files.file_orientation,
|
||||
cameras.camera_model,
|
||||
locations.loc_display_name, locations.loc_name, locations.loc_city, locations.loc_postcode, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type,
|
||||
GROUP_CONCAT(tags.tag_label) AS tags`).
|
||||
|
@ -165,8 +166,14 @@ func (s *Search) FindFiles(count int, offset int) (files []File) {
|
|||
return files
|
||||
}
|
||||
|
||||
func (s *Search) FindFile(id string) (file File) {
|
||||
func (s *Search) FindFileById(id string) (file File) {
|
||||
s.db.Where("id = ?", id).First(&file)
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
func (s *Search) FindFileByHash(fileHash string) (file File) {
|
||||
s.db.Where("file_hash = ?", fileHash).First(&file)
|
||||
|
||||
return file
|
||||
}
|
||||
|
|
|
@ -50,13 +50,13 @@ func ConfigureRoutes(app *gin.Engine, conf *photoprism.Config) {
|
|||
c.JSON(http.StatusOK, files)
|
||||
})
|
||||
|
||||
v1.GET("/files/:id/thumbnail", func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
v1.GET("/files/:hash/thumbnail", func(c *gin.Context) {
|
||||
fileHash := c.Param("hash")
|
||||
size, _ := strconv.Atoi(c.Query("size"))
|
||||
|
||||
search := photoprism.NewSearch(conf.OriginalsPath, conf.GetDb())
|
||||
|
||||
file := search.FindFile(id)
|
||||
file := search.FindFileByHash(fileHash)
|
||||
|
||||
fileName := fmt.Sprintf("%s/%s", conf.OriginalsPath, file.FileName)
|
||||
|
||||
|
@ -69,13 +69,13 @@ func ConfigureRoutes(app *gin.Engine, conf *photoprism.Config) {
|
|||
}
|
||||
})
|
||||
|
||||
v1.GET("/files/:id/square_thumbnail", func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
v1.GET("/files/:hash/square_thumbnail", func(c *gin.Context) {
|
||||
fileHash := c.Param("hash")
|
||||
size, _ := strconv.Atoi(c.Query("size"))
|
||||
|
||||
search := photoprism.NewSearch(conf.OriginalsPath, conf.GetDb())
|
||||
|
||||
file := search.FindFile(id)
|
||||
file := search.FindFileByHash(fileHash)
|
||||
|
||||
fileName := fmt.Sprintf("%s/%s", conf.OriginalsPath, file.FileName)
|
||||
|
||||
|
|
Loading…
Reference in a new issue