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 />
<v-main>
<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>
<GlobalSnackbar />
<router-view></router-view>
@ -76,4 +78,5 @@ export default {
:root {
scrollbar-color: transparent transparent;
}
</style>

View File

@ -58,10 +58,10 @@ export default {
defaultMenu() {
return [
{
title: this.$t("general.download"),
icon: "mdi-download",
title: this.$t("general.print"),
icon: "mdi-printer",
color: "accent",
action: "download",
action: "print",
},
{
title: this.$t("general.link"),
@ -108,8 +108,8 @@ export default {
case "edit":
this.$router.push(`/recipe/${this.slug}` + "?edit=true");
break;
case "download":
await this.downloadJson();
case "print":
this.$router.push(`/recipe/${this.slug}` + "?print=true");
break;
default:
break;
@ -127,25 +127,6 @@ export default {
() => 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>

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>
<div class="text-center">
<div class="text-center d-print-none">
<v-dialog v-model="addRecipe" width="650" @click:outside="reset">
<v-card :loading="processing">
<v-app-bar dark color="primary mb-2">

View File

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

View File

@ -79,6 +79,7 @@
"no": "No",
"ok": "OK",
"options": "Options:",
"print": "Print",
"random": "Random",
"rating": "Rating",
"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-skeleton-loader class="mx-auto" height="700px" type="card"></v-skeleton-loader>
</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">
<RecipeTimeCard
:class="isMobile ? undefined : 'force-bottom'"
@ -36,6 +36,7 @@
/>
<RecipeEditor v-else v-model="recipeDetails" ref="recipeEditor" @upload="getImageFile" />
</v-card>
<PrintView :recipe="recipeDetails" />
</v-container>
</template>
@ -43,6 +44,7 @@
import { api } from "@/api";
import VJsoneditor from "v-jsoneditor";
import RecipeViewer from "@/components/Recipe/RecipeViewer";
import PrintView from "@/components/Recipe/PrintView";
import RecipeEditor from "@/components/Recipe/RecipeEditor";
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
@ -56,6 +58,7 @@ export default {
RecipeEditor,
EditorButtonRow,
RecipeTimeCard,
PrintView,
},
mixins: [user],
inject: {
@ -93,10 +96,17 @@ export default {
imageKey: 1,
};
},
mounted() {
this.getRecipeDetails();
async mounted() {
await this.getRecipeDetails();
this.jsonEditor = false;
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: {
@ -174,6 +184,9 @@ export default {
}
}
},
printPage() {
window.print();
},
},
};
</script>