add print-view component (#407)

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-05-14 21:10:03 -08:00 committed by GitHub
parent 35adc341e6
commit 3804e1d52c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 189 additions and 192 deletions

View File

@ -4,7 +4,9 @@
<TheAppBar /> <TheAppBar />
<v-main> <v-main>
<v-banner v-if="demo" sticky> <v-banner v-if="demo" sticky>
<div class="text-center"><b> This is a Demo of the v0.5.0 (BETA) </b> | Username: changeme@email.com | Password: demo</div> <div class="text-center">
<b> This is a Demo of the v0.5.0 (BETA) </b> | Username: changeme@email.com | Password: demo
</div>
</v-banner> </v-banner>
<GlobalSnackbar /> <GlobalSnackbar />
<router-view></router-view> <router-view></router-view>
@ -76,4 +78,5 @@ export default {
:root { :root {
scrollbar-color: transparent transparent; scrollbar-color: transparent transparent;
} }
</style> </style>

View File

@ -58,10 +58,10 @@ export default {
defaultMenu() { defaultMenu() {
return [ return [
{ {
title: this.$t("general.download"), title: this.$t("general.print"),
icon: "mdi-download", icon: "mdi-printer",
color: "accent", color: "accent",
action: "download", action: "print",
}, },
{ {
title: this.$t("general.link"), title: this.$t("general.link"),
@ -108,8 +108,8 @@ export default {
case "edit": case "edit":
this.$router.push(`/recipe/${this.slug}` + "?edit=true"); this.$router.push(`/recipe/${this.slug}` + "?edit=true");
break; break;
case "download": case "print":
await this.downloadJson(); this.$router.push(`/recipe/${this.slug}` + "?print=true");
break; break;
default: default:
break; break;
@ -127,25 +127,6 @@ export default {
() => console.log("Copied Failed", copyText) () => console.log("Copied Failed", copyText)
); );
}, },
async downloadJson() {
const recipe = await api.recipes.requestDetails(this.slug);
this.downloadString(JSON.stringify(recipe, "", 4), "text/json", recipe.slug + ".json");
},
downloadString(text, fileType, fileName) {
let blob = new Blob([text], { type: fileType });
let a = document.createElement("a");
a.download = fileName;
a.href = URL.createObjectURL(blob);
a.dataset.downloadurl = [fileType, a.download, a.href].join(":");
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(function() {
URL.revokeObjectURL(a.href);
}, 1500);
},
}, },
}; };
</script> </script>

View File

@ -0,0 +1,155 @@
<template>
<div class="container print">
<div>
<h1>
<svg class="icon" viewBox="0 0 24 24">
<path
fill="#E58325"
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
/>
</svg>
{{ recipe.name }}
</h1>
</div>
<div class="time-container">
<RecipeTimeCard :prepTime="recipe.prepTime" :totalTime="recipe.totalTime" :performTime="recipe.performTime" />
</div>
<v-btn
v-if="recipe.recipeYield"
dense
small
:hover="false"
type="label"
:ripple="false"
elevation="0"
color="secondary darken-1"
class="rounded-sm static"
>
{{ recipe.recipeYield }}
</v-btn>
<div>
<vue-markdown :source="recipe.description"> </vue-markdown>
<h2>{{ $t("recipe.ingredients") }}</h2>
<ul>
<li v-for="(ingredient, index) in recipe.recipeIngredient" :key="index">
<v-icon>
mdi-checkbox-blank-outline
</v-icon>
<p>{{ ingredient }}</p>
</li>
</ul>
</div>
<div>
<h2>{{ $t("recipe.instructions") }}</h2>
<div v-for="(step, index) in recipe.recipeInstructions" :key="index">
<h2 v-if="step.title">{{ step.title }}</h2>
<div class="ml-5">
<h3>{{ $t("recipe.step-index", { step: index + 1 }) }}</h3>
<vue-markdown :source="step.text"> </vue-markdown>
</div>
</div>
<br />
<v-divider v-if="recipe.notes.length > 0" class="mb-5 mt-0"></v-divider>
<div v-for="(note, index) in recipe.notes" :key="index + 'note'">
<h3>{{ note.title }}</h3>
<vue-markdown :source="note.text"> </vue-markdown>
</div>
</div>
</div>
</template>
<script>
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
import VueMarkdown from "@adapttive/vue-markdown";
export default {
components: {
RecipeTimeCard,
VueMarkdown,
},
props: {
recipe: Object,
},
};
</script>
<style>
@media print {
body,
html {
margin-top: -40px !important;
}
}
h1 {
margin-top: 0 !important;
display: -webkit-box;
display: flex;
font-size: 2rem;
letter-spacing: -0.015625em;
font-weight: 300;
padding: 0;
}
h2 {
margin-bottom: 0.25rem;
}
h3 {
margin-bottom: 0.25rem;
}
ul {
padding-left: 1rem;
}
li {
display: -webkit-box;
display: -webkit-flex;
margin-left: 0;
margin-bottom: 0.5rem;
}
li p {
margin-left: 0.25rem;
margin-bottom: 0 !important;
}
p {
margin: 0;
font-size: 1rem;
letter-spacing: 0.03125em;
font-weight: 400;
}
.icon {
margin-top: auto;
margin-bottom: auto;
margin-right: 0.5rem;
height: 3rem;
width: 3rem;
}
.time-container {
display: flex;
justify-content: left;
}
.time-chip {
border-radius: 0.25rem;
border-color: black;
border: 1px;
border-top: 1px;
}
.print {
display: none;
}
@media print {
.print {
display: initial;
}
}
</style>

View File

@ -1,162 +0,0 @@
<template>
<div>
<v-card flat class="d-print-none">
<v-card-text>
<v-row align="center" justify="center">
<v-btn left color="accent lighten-1 " class="ma-1 image-action" @click="$emit('exit')">
<v-icon> mdi-arrow-left </v-icon>
</v-btn>
<v-card flat class="text-center" align-center>
<v-card-text>Font Size</v-card-text>
<v-card-text>
<v-btn class="mx-2" fab dark x-small color="primary" @click="subtractFontSize">
<v-icon dark> mdi-minus </v-icon>
</v-btn>
<v-btn class="mx-2" fab dark x-small color="primary" @click="addFontSize">
<v-icon dark> mdi-plus </v-icon>
</v-btn>
</v-card-text>
</v-card>
</v-row>
</v-card-text>
</v-card>
<v-card flat>
<v-row dense align="center">
<v-col md="10" sm="10">
<v-card flat>
<v-card-title> {{ recipe.name }} </v-card-title>
<v-card-text> {{ recipe.description }} </v-card-text>
<v-divider></v-divider>
</v-card>
</v-col>
<v-col md="1" sm="1" justify-end>
<v-img :src="getImage(recipe.image)" max-height="200" max-width="300"> </v-img>
</v-col>
</v-row>
</v-card>
<v-card flat align>
<v-card-text>
<v-row class="mt-n6">
<v-col>
<v-btn
v-if="recipe.recipeYield"
dense
small
:hover="false"
type="label"
:ripple="false"
elevation="0"
color="secondary darken-1"
class="rounded-sm static"
>
{{ recipe.recipeYield }}
</v-btn>
</v-col>
<v-rating
class="mr-2 align-end static"
color="secondary darken-1"
background-color="secondary lighten-3"
length="5"
:value="recipe.rating"
></v-rating>
</v-row>
<h2 class="mt-1">Ingredients</h2>
<v-row>
<v-list dense class="column-wrapper align-start">
<v-list-item
v-for="(ingredient, index) in recipe.recipeIngredient"
:key="generateKey('ingredient', index)"
hide-details
class="mb-n3 print-text"
:label="ingredient"
>
<v-list-item-icon class="mr-1">
<v-icon> mdi-minus </v-icon>
</v-list-item-icon>
{{ ingredient }}
</v-list-item>
</v-list>
</v-row>
<v-row dense>
<v-col cols="12">
<div v-if="recipe.categories[0]">
<h2 class="mt-4">Categories</h2>
<v-chip class="ma-1" color="primary" dark v-for="category in recipe.categories" :key="category">
{{ category }}
</v-chip>
</div>
<div v-if="recipe.tags[0]">
<h2 class="mt-4">Tags</h2>
<v-chip class="ma-1" color="primary" dark v-for="tag in recipe.tags" :key="tag">
{{ tag }}
</v-chip>
</div>
<h2 v-if="recipe.notes[0]" class="my-2">Notes</h2>
<v-card flat class="mt-1" v-for="(note, index) in recipe.notes" :key="generateKey('note', index)">
<v-card-title> {{ note.title }}</v-card-title>
<v-card-text>
{{ note.text }}
</v-card-text>
</v-card>
</v-col>
<v-col cols="12">
<h2 class="mb-4">Instructions</h2>
<v-card
v-for="(step, index) in recipe.recipeInstructions"
:key="generateKey('step', index)"
class="my-n4"
flat
>
<v-card-title class="my-n4">Step: {{ index + 1 }}</v-card-title>
<v-card-text class="my-n4">{{ step.text }}</v-card-text>
</v-card>
</v-col>
</v-row>
</v-card-text>
</v-card>
</div>
</template>
<script>
import { utils } from "@/utils";
import { api } from "@/api";
export default {
props: {
recipe: Object,
},
data() {
return {
fontSize: 1.0,
};
},
methods: {
getImage(image) {
if (image) {
return api.recipes.recipeImage(image) + "?rnd=" + this.imageKey;
}
},
generateKey(item, index) {
return utils.generateUniqueKey(item, index);
},
addFontSize() {
this.fontSize += 0.2;
},
subtractFontSize() {
this.fontSize -= 0.2;
},
},
};
</script>
<style scoped>
.column-wrapper {
column-count: 2;
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="text-center"> <div class="text-center d-print-none">
<v-dialog v-model="addRecipe" width="650" @click:outside="reset"> <v-dialog v-model="addRecipe" width="650" @click:outside="reset">
<v-card :loading="processing"> <v-card :loading="processing">
<v-app-bar dark color="primary mb-2"> <v-app-bar dark color="primary mb-2">

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="d-print-none no-print">
<v-navigation-drawer v-model="showSidebar" width="180px" clipped app> <v-navigation-drawer v-model="showSidebar" width="180px" clipped app>
<template v-slot:prepend> <template v-slot:prepend>
<v-list-item two-line v-if="isLoggedIn" to="/admin/profile"> <v-list-item two-line v-if="isLoggedIn" to="/admin/profile">
@ -231,4 +231,10 @@ export default {
bottom: 0 !important; bottom: 0 !important;
width: 100%; width: 100%;
} }
@media print {
.no-print {
display: none;
}
}
</style> </style>

View File

@ -79,6 +79,7 @@
"no": "No", "no": "No",
"ok": "OK", "ok": "OK",
"options": "Options:", "options": "Options:",
"print": "Print",
"random": "Random", "random": "Random",
"rating": "Rating", "rating": "Rating",
"recent": "Recent", "recent": "Recent",

View File

@ -3,7 +3,7 @@
<v-card v-if="skeleton" :color="`white ${theme.isDark ? 'darken-2' : 'lighten-4'}`" class="pa-3"> <v-card v-if="skeleton" :color="`white ${theme.isDark ? 'darken-2' : 'lighten-4'}`" class="pa-3">
<v-skeleton-loader class="mx-auto" height="700px" type="card"></v-skeleton-loader> <v-skeleton-loader class="mx-auto" height="700px" type="card"></v-skeleton-loader>
</v-card> </v-card>
<v-card v-else id="myRecipe"> <v-card v-else id="myRecipe" class="d-print-none">
<v-img height="400" :src="getImage(recipeDetails.slug)" class="d-print-none" :key="imageKey"> <v-img height="400" :src="getImage(recipeDetails.slug)" class="d-print-none" :key="imageKey">
<RecipeTimeCard <RecipeTimeCard
:class="isMobile ? undefined : 'force-bottom'" :class="isMobile ? undefined : 'force-bottom'"
@ -36,6 +36,7 @@
/> />
<RecipeEditor v-else v-model="recipeDetails" ref="recipeEditor" @upload="getImageFile" /> <RecipeEditor v-else v-model="recipeDetails" ref="recipeEditor" @upload="getImageFile" />
</v-card> </v-card>
<PrintView :recipe="recipeDetails" />
</v-container> </v-container>
</template> </template>
@ -43,6 +44,7 @@
import { api } from "@/api"; import { api } from "@/api";
import VJsoneditor from "v-jsoneditor"; import VJsoneditor from "v-jsoneditor";
import RecipeViewer from "@/components/Recipe/RecipeViewer"; import RecipeViewer from "@/components/Recipe/RecipeViewer";
import PrintView from "@/components/Recipe/PrintView";
import RecipeEditor from "@/components/Recipe/RecipeEditor"; import RecipeEditor from "@/components/Recipe/RecipeEditor";
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue"; import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
import EditorButtonRow from "@/components/Recipe/EditorButtonRow"; import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
@ -56,6 +58,7 @@ export default {
RecipeEditor, RecipeEditor,
EditorButtonRow, EditorButtonRow,
RecipeTimeCard, RecipeTimeCard,
PrintView,
}, },
mixins: [user], mixins: [user],
inject: { inject: {
@ -93,10 +96,17 @@ export default {
imageKey: 1, imageKey: 1,
}; };
}, },
mounted() {
this.getRecipeDetails(); async mounted() {
await this.getRecipeDetails();
this.jsonEditor = false; this.jsonEditor = false;
this.form = this.$route.query.edit === "true" && this.loggedIn; this.form = this.$route.query.edit === "true" && this.loggedIn;
console.log(this.$route.query.print);
if (this.$route.query.print) {
this.printPage();
this.$router.push(this.$route.path);
}
}, },
watch: { watch: {
@ -174,6 +184,9 @@ export default {
} }
} }
}, },
printPage() {
window.print();
},
}, },
}; };
</script> </script>