Files
web/app/lib/email/templates/Template1.vue
2026-03-02 09:45:33 +07:00

431 lines
12 KiB
Vue

<template>
<div v-if="previewMode">
<component :is="'style'" v-html="styles" />
<div
class="container py-6"
:style="{
minHeight: '100%',
backgroundColor: 'hsl(0, 0%, 97%)',
fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif',
}"
>
<table
role="presentation"
cellspacing="0"
cellpadding="0"
border="0"
:style="{
width: '100%',
maxWidth: '100%',
margin: '0 auto',
}"
>
<tbody>
<tr>
<td>
<table
role="presentation"
class="email-container gmail-fix"
cellspacing="0"
cellpadding="0"
border="0"
:style="{
margin: '0 auto',
width: '100%',
maxWidth: '680px',
backgroundColor: '#ffffff',
borderRadius: '16px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
overflow: 'hidden',
}"
>
<tbody>
<tr class="header-row">
<td :style="{ padding: '0' }">
<div
class="header-image"
:style="{
background: '#f3f4f6',
position: 'relative',
width: '100%',
height: '425px',
overflow: 'hidden',
}"
>
<img
v-if="hasValidImage"
:src="content.imageUrl"
alt="image"
:style="{
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
}"
/>
<div
v-else
class="is-flex is-justify-content-center is-align-items-center"
:style="{
height: '100%',
background: 'rgba(0, 0, 0, 0.3)',
opacity: 0.15,
}"
>
<SvgIcon v-bind="{ name: 'image.svg', type: 'black', size: 180 }" />
</div>
</div>
</td>
</tr>
<tr v-if="content.message">
<td class="content-padding" :style="{ paddingBottom: '20px', paddingTop: '30px' ,paddingRight: '25px', paddingLeft: '25px'}">
<div
:style="{
border: '1px solid #e5e7eb',
borderRadius: '12px',
backgroundColor: '#ffffff',
overflow: 'hidden',
}"
>
<div :style="{ padding: '15px' }">
<div
class="message-content"
:style="{
fontSize: '16px',
margin: '0',
color: '#374151',
lineHeight: '1.6',
wordBreak: 'break-word',
}"
>
<div class="content" v-html="processedMessage" />
</div>
</div>
<div
v-if="hasValidLinks"
:style="{
padding: '15px',
paddingLeft: '35px',
borderTop: '1px solid #e5e7eb',
backgroundColor: '#f9fafb',
}"
>
<div>
<div
v-for="(link, index) in validLinks"
:key="index"
:style="{ width: '100%', marginBottom: '8px' }"
>
<ul :style="{ paddingLeft: '20px', margin: 0 }">
<li :style="{ listStyleType: 'disc' }">
<a
:style="{
color: '#2563eb!important',
fontWeight: '600',
textDecoration: 'underline',
display: 'inline-block',
wordBreak: 'break-word',
fontSize: '14px',
}"
target="_blank"
:href="link"
rel="noopener noreferrer"
>
{{ getLinkText(index) }}
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td class="company-padding" :style="{ paddingBottom: '20px' }">
<div
class="company-info"
:style="{
background: 'linear-gradient(to right, #000000, #0f9b0f)',
color: '#ffffff',
borderRadius: '12px',
}"
>
<div
class="logo-container"
:style="{
width: '100%',
height: '100%',
marginBottom: '15px',
backgroundColor: '#ffffff',
borderRadius: '8px',
overflow: 'hidden',
}"
>
<img
:src="imageUrl"
alt="Utopia Footer"
:style="{
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
}"
/>
</div>
</div>
</td>
</tr>
<!-- <tr>
<td
:style="{
backgroundColor: '#f9fafb',
padding: '20px',
borderTop: '1px solid #e5e7eb',
textAlign: 'center',
}"
>
<p
:style="{
color: '#9ca3af',
fontSize: '11px',
margin: '0',
lineHeight: '1.5',
}"
>
©2025 Utopia. Tất cả các quyền được bảo lưu.
</p>
</td>
</tr> -->
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { imageUrl } from '@/components/marketing/email/Email.utils';
import { computed } from "vue";
interface KeywordItem {
keyword: string;
value: string;
}
interface Template1Content {
subject?: string;
message?: string;
imageUrl?: string | null;
linkUrl?: string[];
textLinkUrl?: string[];
keyword?: KeywordItem[];
}
interface Props {
content: Template1Content;
previewMode?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
previewMode: false,
});
const hasValidLinks = computed(() => {
return props.content.linkUrl && props.content.linkUrl.some((link) => link && link.trim() !== "");
});
const hasValidImage = computed(() => {
return props.content.imageUrl && props.content.imageUrl.trim() !== "";
});
const replaceKeywords = (content: string): string => {
if (!content) return "";
let replacedContent = content;
if (props.content.keyword && Array.isArray(props.content.keyword)) {
props.content.keyword.forEach((kw) => {
if (typeof kw !== "string" && kw.keyword && kw.value) {
const regex = new RegExp(`\\{\\{${kw.keyword}\\}\\}`, "g");
replacedContent = replacedContent.replace(regex, kw.value);
}
});
}
return replacedContent;
};
const processedMessage = computed(() => replaceKeywords(props.content.message || ""));
const validLinks = computed(() => {
if (!props.content.linkUrl || props.content.linkUrl.length === 0) return [];
return props.content.linkUrl.filter((link) => link && link.trim() !== "");
});
const getLinkText = (index: number): string => {
return props.content.textLinkUrl && props.content.textLinkUrl[index] && props.content.textLinkUrl[index].trim() !== ""
? props.content.textLinkUrl[index]
: `Link ${index + 1}`;
};
const styles = `
ol{
list-style: decimal;
}
li{
list-style: disc;
}
.greeting-padding{
padding: 30px 25px 20px;
}
.content-padding, .company-padding {
padding: 0px 25px 20px;
}
.message-content p {
margin: 0 0 12px 0;
line-height: 1.6;
}
.message-content p:last-child {
margin-bottom: 0;
}
.message-content strong {
font-weight: 700;
}
.message-content em {
font-style: italic;
}
.message-content u {
text-decoration: underline;
}
.message-content s {
text-decoration: line-through;
}
.message-content ol,
.message-content ul {
margin: 12px 0;
padding-left: 25px;
}
.message-content ol li,
.message-content ul li {
margin-bottom: 8px;
line-height: 1.6;
}
.message-content a {
color: #2563eb;
text-decoration: underline;
}
.message-content h1 {
font-size: 28px;
font-weight: 700;
margin: 20px 0 12px 0;
line-height: 1.3;
color: #1f2937;
}
.message-content h2 {
font-size: 24px;
font-weight: 700;
margin: 18px 0 12px 0;
line-height: 1.3;
color: #1f2937;
}
.message-content h3 {
font-size: 20px;
font-weight: 700;
margin: 16px 0 12px 0;
line-height: 1.3;
color: #1f2937;
}
.message-content .ql-align-center {
text-align: center;
}
.message-content .ql-align-right {
text-align: right;
}
.message-content .ql-align-justify {
text-align: justify;
}
.message-content .ql-indent-1 {
padding-left: 3em;
}
.message-content .ql-indent-2 {
padding-left: 6em;
}
@media only screen and (max-width: 768px) {
.email-container {
width: 100% !important;
max-width: 100% !important;
}
.greeting-padding{
padding: 20px 0 0 0 !important;
}
.content-padding {
padding: 20px 0px !important;
}
.company-padding {
padding: 0px 0px 20px 0px !important;
}
.company-info {
padding: 20px 10px !important;
}
.greeting-title{
padding: 10px !important;
margin: 0 !important;
}
.header-image {
width: 100% !important;
height: 250px !important;
}
.greeting-text {
font-size: 12px !important;
}
.message-text {
font-size: 12px !important;
}
.logo-container {
width: 150px !important;
height: 45px !important;
}
.company-name {
font-size: 12px !important;
color: #ffffff !important;
}
.contact-info {
font-size: 11px !important;
color: #ffffff !important;
}
}
`;
</script>