feat(ui): 添加友链申请表单中想说的话字段

在 `FriendsSection.vue` 中添加了新的表单字段“想说的话”,用户可以输入最多50字的信息。同时更新了相关的样式,以支持 `textarea` 的显示和交互,包括输入框的样式和字符计数的显示。

在 `send-mail.ts` 中添加了对新字段 `message` 的处理,确保其在邮件内容中正确显示,并在邮件的HTML部分增加了对 `message` 字段的引用。

在生成的邮件内容中增加了友链申请者的“想说的话”信息,方便接收者查看。
This commit is contained in:
2025-12-12 23:29:42 +08:00
parent 99ffc73e76
commit 55f4307b13
4 changed files with 61 additions and 19 deletions

View File

@@ -11,12 +11,12 @@ export default defineNuxtConfig({
link: [{ rel: "icon", href: siteConfig.siteMeta.icon }],
},
},
nitro: {
prerender: {
crawlLinks: true,
routes: ["/sitemap.xml", "/rss.xml"],
},
},
// nitro: {
// prerender: {
// crawlLinks: true,
// routes: ["/sitemap.xml", "/rss.xml"],
// },
// },
runtimeConfig: {
smtpHost: process.env.SMTP_HOST ?? "",
smtpPort: Number(process.env.SMTP_PORT ?? 465),

View File

@@ -26,6 +26,13 @@
头像链接
<input v-model="form.avatar" type="url" placeholder="可选,展示头像" />
</label>
<label>
想说的话
<div class="textarea-wrapper">
<textarea v-model="form.message" placeholder="可选最多50字" maxlength="50"></textarea>
<span class="char-count">{{ form.message?.length || 0 }}/50</span>
</div>
</label>
<div class="form-actions">
<button type="submit" class="primary" :disabled="loading">
{{ loading ? "提交中..." : "提交申请" }}
@@ -75,6 +82,7 @@ const form = reactive({
desc: "",
email: "",
avatar: "",
message: "",
});
const displayedFriends = ref([]);
@@ -108,6 +116,7 @@ const submitForm = async () => {
desc: form.desc,
email: form.email,
avatar: form.avatar,
message: form.message,
}),
});
if (!resp.ok) throw new Error("send failed");
@@ -192,15 +201,41 @@ h2 {
color: inherit;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.friend-form input::placeholder {
.textarea-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.friend-form textarea {
flex: 1;
padding: 8px 10px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.06);
color: inherit;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
font-family: inherit;
height: 36px;
resize: none;
overflow: auto;
}
.friend-form input::placeholder,
.friend-form textarea::placeholder {
color: rgba(232, 238, 252, 0.7);
}
.friend-form input:focus {
.friend-form input:focus,
.friend-form textarea:focus {
outline: none;
border-color: rgba(124, 193, 255, 0.8);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 0 2px rgba(124, 193, 255, 0.25);
}
.char-count {
font-size: 12px;
color: rgba(232, 238, 252, 0.6);
white-space: nowrap;
flex-shrink: 0;
}
.form-actions {
grid-column: 1 / -1;
display: flex;

View File

@@ -19,6 +19,7 @@ type SendMailPayload = {
desc?: string;
email?: string;
avatar?: string;
message?: string;
};
const ensureValue = (value?: string, fallback = "未填写") => (value?.trim() ? value.trim() : fallback);
@@ -35,7 +36,7 @@ export default defineEventHandler(async (event) => {
}
const payload = (await readBody<SendMailPayload>(event)) || {};
const { name, url, desc, email, avatar } = payload;
const { name, url, desc, email, avatar, message } = payload;
if (!name?.trim() || !url?.trim() || !email?.trim()) {
throw createError({
@@ -45,15 +46,7 @@ export default defineEventHandler(async (event) => {
}
const config = useRuntimeConfig() as MailConfig;
const {
smtpHost,
smtpPort: configSmtpPort,
smtpUser,
smtpPass,
senderEmail,
adminEmail,
smtpSecure,
} = config;
const { smtpHost, smtpPort: configSmtpPort, smtpUser, smtpPass, senderEmail, adminEmail, smtpSecure } = config;
const smtpPort = Number(configSmtpPort ?? 465);
if (!smtpHost || !smtpUser || !smtpPass || !senderEmail || !adminEmail) {
@@ -72,12 +65,26 @@ export default defineEventHandler(async (event) => {
};
const transporter = nodemailer.createTransport(smtpOptions);
const friendEntry = `{
name: "${ensureValue(name).replace(/"/g, '\\"')}",
url: "${ensureValue(url).replace(/"/g, '\\"')}",
desc: "${ensureValue(desc).replace(/"/g, '\\"')}",
avatar: "${ensureValue(avatar).replace(/"/g, '\\"')}",
},`;
const htmlMessage = `
<p>一个新的友链申请已提交,以下是可直接复制到项目中的配置:</p>
<pre style="background: #f5f5f5; padding: 12px; border-radius: 4px; overflow: auto;">
<code>${friendEntry}</code>
</pre>
<hr style="margin: 20px 0;" />
<p><strong>申请者信息:</strong></p>
<p><strong>名称:</strong>${ensureValue(name)}</p>
<p><strong>邮箱:</strong>${ensureValue(email)}</p>
<p><strong>站点:</strong><a href="${ensureValue(url)}">${ensureValue(url)}</a></p>
<p><strong>描述:</strong>${ensureValue(desc)}</p>
<p><strong>头像:</strong>${ensureValue(avatar)}</p>
<p><strong>想说的话:</strong>${ensureValue(message)}</p>
<p><strong>时间:</strong>${new Date().toISOString()}</p>
`;

View File

@@ -1,6 +1,6 @@
{
"version": 2,
"framework": "vite",
"buildCommand": "nuxt generate",
"routes": [
{
"src": "/api/(.*)",