完成70%
This commit is contained in:
@@ -1,26 +1,27 @@
|
||||
<!-- 页面编号:F-06;用途:新建与编辑谱文。 -->
|
||||
<!-- 页面编号:F-06;用途:按已声明 articleTitle/articleContent 创建谱文。 -->
|
||||
<template>
|
||||
<view
|
||||
class="article-editor-page"
|
||||
:class="`article-editor-state--${editorState}`"
|
||||
>
|
||||
<view class="article-editor-page" :class="`article-editor-state--${editorState}`">
|
||||
<ModulePageBackground module="family" />
|
||||
<view class="article-editor-page__header">
|
||||
<PageHeader title="编辑谱文" custom-back @back="requestBack" />
|
||||
</view>
|
||||
|
||||
<view v-if="editorState === 'loading'" class="article-editor-loading">
|
||||
<AppLoading
|
||||
text="正在打开谱文编辑器"
|
||||
description="请稍候,草稿正在展开。"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-else-if="editorState === 'preview' || editorState === 'invalid'"
|
||||
class="article-editor-content"
|
||||
>
|
||||
<view class="editor-result-card">
|
||||
<view class="article-editor-page__header"><PageHeader title="新建谱文" custom-back @back="requestBack" /></view>
|
||||
<view class="article-editor-content">
|
||||
<view v-if="editorState === 'form'" class="editor-panel">
|
||||
<view class="editor-panel__body">
|
||||
<text class="editor-eyebrow">服务端创建</text>
|
||||
<text class="editor-title">把值得传承的故事写下来</text>
|
||||
<text class="editor-intro">本页只发送 Apifox 已声明且可映射的文章标题与正文。分类需要 `categoryId`,当前没有可用分类 owner,故不展示或猜测分类。</text>
|
||||
<view class="editor-field">
|
||||
<text class="editor-field__label">文章标题</text>
|
||||
<view class="editor-control"><input v-model="form.title" maxlength="40" placeholder="请输入文章标题" placeholder-class="editor-placeholder" @input="submitError = ''" /></view>
|
||||
</view>
|
||||
<view class="editor-field">
|
||||
<text class="editor-field__label">正文内容</text>
|
||||
<view class="editor-control editor-control--textarea"><textarea v-model="form.content" auto-height maxlength="1200" placeholder="记录家训、往事或想留给后人的话" placeholder-class="editor-placeholder" @input="submitError = ''" /></view>
|
||||
</view>
|
||||
<text v-if="submitError" class="editor-save-error">{{ submitError }}</text>
|
||||
<AppButton block :label="isSubmitting ? '正在提交' : '提交谱文'" :disabled="isSubmitting" @click="submit" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="editor-result-card">
|
||||
<view class="editor-result-card__body">
|
||||
<text class="editor-eyebrow">{{ resultCopy.eyebrow }}</text>
|
||||
<text class="editor-result-card__title">{{ resultCopy.title }}</text>
|
||||
@@ -29,89 +30,6 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="article-editor-content">
|
||||
<view class="editor-panel">
|
||||
<view class="editor-panel__body">
|
||||
<text class="editor-eyebrow">草稿 · 未发布</text>
|
||||
<text class="editor-title">把值得传承的故事写下来</text>
|
||||
<text class="editor-intro"
|
||||
>补充标题、分类和正文;当前只校验并生成本地预览。</text
|
||||
>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-field__label">文章标题</text>
|
||||
<view
|
||||
class="editor-control"
|
||||
:class="{ 'editor-control--error': fieldErrors.title }"
|
||||
>
|
||||
<input
|
||||
v-model="form.title"
|
||||
maxlength="40"
|
||||
placeholder="请输入文章标题"
|
||||
placeholder-class="editor-placeholder"
|
||||
@input="clearFieldError('title')"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="fieldErrors.title" class="editor-field__error">{{
|
||||
fieldErrors.title
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-field__label">文章分类</text>
|
||||
<view
|
||||
class="editor-control"
|
||||
:class="{ 'editor-control--error': fieldErrors.category }"
|
||||
>
|
||||
<input
|
||||
v-model="form.category"
|
||||
maxlength="20"
|
||||
placeholder="如:家风家训"
|
||||
placeholder-class="editor-placeholder"
|
||||
@input="clearFieldError('category')"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="fieldErrors.category" class="editor-field__error">{{
|
||||
fieldErrors.category
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-field__label">正文内容</text>
|
||||
<view
|
||||
class="editor-control editor-control--textarea"
|
||||
:class="{ 'editor-control--error': fieldErrors.content }"
|
||||
>
|
||||
<textarea
|
||||
v-model="form.content"
|
||||
auto-height
|
||||
maxlength="1200"
|
||||
placeholder="记录家训、往事或想留给后人的话"
|
||||
placeholder-class="editor-placeholder"
|
||||
@input="clearFieldError('content')"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="fieldErrors.content" class="editor-field__error">{{
|
||||
fieldErrors.content
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<text class="editor-draft-note"
|
||||
>当前为未发布草稿;真实草稿保存将在接口阶段接入。</text
|
||||
>
|
||||
<text v-if="editorState === 'error'" class="editor-save-error"
|
||||
>保存失败,已填写内容仍保留,请稍后重试。</text
|
||||
>
|
||||
<AppButton
|
||||
block
|
||||
:label="actionLabel"
|
||||
:disabled="isSubmitting"
|
||||
@click="submit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="discardVisible"
|
||||
@@ -127,87 +45,38 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
|
||||
import { computed, onUnmounted, reactive, ref } from "vue";
|
||||
import { onBackPress, onLoad } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import {
|
||||
findFamilyArticleFixture,
|
||||
getGenealogyFixtureAccess,
|
||||
} from "@/data/mock.js";
|
||||
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
|
||||
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
|
||||
import {
|
||||
goBack,
|
||||
handleBackPress,
|
||||
returnTo,
|
||||
runBackGuard,
|
||||
} from "@/utils/navigation.js";
|
||||
import { goBack, handleBackPress, returnTo, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const draftFixture = {
|
||||
title: "汤氏家训辑录",
|
||||
category: "家风家训",
|
||||
content:
|
||||
"孝友传家,勤俭立业;敬祖睦宗,诚实待人。愿后人常怀感恩,彼此扶持。",
|
||||
};
|
||||
const editorState = ref("form");
|
||||
const genealogyId = ref("");
|
||||
const articleId = ref("");
|
||||
const editorMode = ref("create");
|
||||
const isSubmitting = ref(false);
|
||||
const discardVisible = ref(false);
|
||||
const allowedStates = new Set([
|
||||
"draft",
|
||||
"loading",
|
||||
"validation",
|
||||
"error",
|
||||
"preview",
|
||||
]);
|
||||
const form = reactive({ title: "", category: "", content: "" });
|
||||
const fieldErrors = reactive({ title: "", category: "", content: "" });
|
||||
const baseline = ref("");
|
||||
let saveTimer = null;
|
||||
|
||||
const actionLabel = computed(() =>
|
||||
isSubmitting.value
|
||||
? "正在校验…"
|
||||
: editorState.value === "error"
|
||||
? "重新校验"
|
||||
: "生成本地预览",
|
||||
);
|
||||
const formSnapshot = computed(() => JSON.stringify(form));
|
||||
const isDirty = computed(() =>
|
||||
Boolean(baseline.value) && formSnapshot.value !== baseline.value,
|
||||
);
|
||||
const hasValidContext = computed(() =>
|
||||
Boolean(
|
||||
genealogyId.value &&
|
||||
["owner", "member"].includes(
|
||||
getGenealogyFixtureAccess(genealogyId.value).accessRole,
|
||||
) &&
|
||||
(editorMode.value === "create" ||
|
||||
(editorMode.value === "edit" && articleId.value)),
|
||||
),
|
||||
);
|
||||
const resultCopy = computed(() =>
|
||||
editorState.value === "preview"
|
||||
? {
|
||||
eyebrow: "本地流程预览",
|
||||
title: "谱文内容已通过本地校验",
|
||||
copy: "当前尚未提交服务器,返回后不会新增或修改谱文。",
|
||||
action:
|
||||
editorMode.value === "edit"
|
||||
? "返回原谱文(不保存)"
|
||||
: "返回谱文列表(不保存)",
|
||||
}
|
||||
: {
|
||||
eyebrow: "谱文入口无效",
|
||||
title: "没有找到要编辑的谱文上下文",
|
||||
copy: "请从当前家谱的谱文列表重新进入,页面不会创建无归属内容。",
|
||||
action: "返回上一页",
|
||||
},
|
||||
const submitError = ref("");
|
||||
const form = reactive({ title: "", content: "" });
|
||||
const requestController = createRequestController();
|
||||
const isDirty = computed(() => Boolean(form.title.trim() || form.content.trim()));
|
||||
const hasValidContext = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
|
||||
const resultCopy = computed(() => editorState.value === "success"
|
||||
? {
|
||||
eyebrow: "服务端已接受",
|
||||
title: "谱文已提交",
|
||||
copy: "服务端已返回成功信封。谱文列表与详情仍缺展示 DTO,返回后不会生成本地文章卡片。",
|
||||
action: "返回谱文列表",
|
||||
}
|
||||
: {
|
||||
eyebrow: "谱文入口无效",
|
||||
title: "无法创建谱文",
|
||||
copy: "当前只有新建操作可映射;编辑需要可靠的文章详情和文章 ID,暂不从 fixture 进入。",
|
||||
action: "返回上一页",
|
||||
},
|
||||
);
|
||||
const discardConfirmation = createDiscardConfirmation((visible) => {
|
||||
discardVisible.value = visible;
|
||||
@@ -216,263 +85,72 @@ const requestDiscardConfirmation = discardConfirmation.request;
|
||||
const confirmDiscard = discardConfirmation.confirm;
|
||||
const cancelDiscard = discardConfirmation.cancel;
|
||||
|
||||
const fillDraft = () => {
|
||||
Object.assign(form, draftFixture);
|
||||
};
|
||||
const showAllFieldErrors = () => {
|
||||
fieldErrors.title = "请填写文章标题";
|
||||
fieldErrors.category = "请填写文章分类";
|
||||
fieldErrors.content = "请填写正文内容";
|
||||
};
|
||||
|
||||
onLoad((query) => {
|
||||
genealogyId.value = String(query.genealogyId || "");
|
||||
articleId.value = String(query.articleId || "");
|
||||
editorMode.value = String(query.mode || "");
|
||||
const article =
|
||||
editorMode.value === "edit"
|
||||
? findFamilyArticleFixture(genealogyId.value, articleId.value)
|
||||
: null;
|
||||
const modeIsValid =
|
||||
(editorMode.value === "create" && !articleId.value) ||
|
||||
(editorMode.value === "edit" && Boolean(article));
|
||||
if (!modeIsValid || !hasValidContext.value) {
|
||||
editorState.value = "invalid";
|
||||
return;
|
||||
}
|
||||
const requestedState = allowedStates.has(query.state) ? query.state : "form";
|
||||
if (article) {
|
||||
Object.assign(form, {
|
||||
title: article.title,
|
||||
category: article.category,
|
||||
content: article.paragraphs.join("\n\n"),
|
||||
});
|
||||
} else if (["draft", "error", "preview"].includes(requestedState)) {
|
||||
fillDraft();
|
||||
}
|
||||
baseline.value = formSnapshot.value;
|
||||
if (editorMode.value === "create" && requestedState === "draft") {
|
||||
baseline.value = JSON.stringify({ title: "", category: "", content: "" });
|
||||
}
|
||||
if (requestedState === "validation") showAllFieldErrors();
|
||||
editorState.value = requestedState;
|
||||
genealogyId.value = String(query?.genealogyId || "");
|
||||
if (!hasValidContext.value || query?.mode !== "create") editorState.value = "invalid";
|
||||
});
|
||||
|
||||
const clearFieldError = (field) => {
|
||||
fieldErrors[field] = "";
|
||||
if (
|
||||
editorState.value === "error" ||
|
||||
editorState.value === "validation"
|
||||
) {
|
||||
editorState.value = "form";
|
||||
}
|
||||
};
|
||||
const validate = () => {
|
||||
fieldErrors.title = form.title.trim() ? "" : "请填写文章标题";
|
||||
fieldErrors.category = form.category.trim() ? "" : "请填写文章分类";
|
||||
fieldErrors.content = form.content.trim() ? "" : "请填写正文内容";
|
||||
return !fieldErrors.title && !fieldErrors.category && !fieldErrors.content;
|
||||
};
|
||||
const submit = () => {
|
||||
const submit = async () => {
|
||||
if (isSubmitting.value || !hasValidContext.value) return;
|
||||
if (!validate()) {
|
||||
editorState.value = "validation";
|
||||
const articleTitle = form.title.trim();
|
||||
const articleContent = form.content.trim();
|
||||
if (!articleTitle || !articleContent) {
|
||||
submitError.value = !articleTitle ? "请填写文章标题" : "请填写正文内容";
|
||||
return;
|
||||
}
|
||||
isSubmitting.value = true;
|
||||
const submitSnapshot = formSnapshot.value;
|
||||
const timer = setTimeout(() => {
|
||||
if (saveTimer !== timer) return;
|
||||
editorState.value = submitSnapshot ? "preview" : "error";
|
||||
submitError.value = "";
|
||||
try {
|
||||
await appApi.createArticle(genealogyId.value, { articleTitle, articleContent }, { requestController });
|
||||
form.title = "";
|
||||
form.content = "";
|
||||
editorState.value = "success";
|
||||
} catch (error) {
|
||||
if (!isRequestCancelled(error)) submitError.value = error?.message || "谱文提交失败,请稍后重试。";
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
saveTimer = null;
|
||||
}, 320);
|
||||
saveTimer = timer;
|
||||
}
|
||||
};
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: isDirty.value,
|
||||
submitting: isSubmitting.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": requestDiscardConfirmation,
|
||||
});
|
||||
const returnToTarget = async () => {
|
||||
const confirmed = isDirty.value ? await requestDiscardConfirmation() : true;
|
||||
if (!confirmed) return false;
|
||||
return editorMode.value === "edit"
|
||||
? returnTo("F05", {
|
||||
genealogyId: genealogyId.value,
|
||||
articleId: articleId.value,
|
||||
})
|
||||
: returnTo("F04", { genealogyId: genealogyId.value });
|
||||
};
|
||||
const handleResultAction = () =>
|
||||
editorState.value === "preview" ? returnToTarget() : goBack();
|
||||
|
||||
const requestBack = () => runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: isDirty.value,
|
||||
submitting: isSubmitting.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": requestDiscardConfirmation,
|
||||
});
|
||||
const handleResultAction = () => editorState.value === "success"
|
||||
? returnTo("F04", { genealogyId: genealogyId.value })
|
||||
: goBack();
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
|
||||
onUnload(() => {
|
||||
if (saveTimer) clearTimeout(saveTimer);
|
||||
onUnmounted(() => {
|
||||
requestController.abort();
|
||||
discardConfirmation.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||||
|
||||
.article-editor-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
background: $paper;
|
||||
}
|
||||
.article-editor-page__header,
|
||||
.article-editor-loading,
|
||||
.article-editor-content {
|
||||
z-index: 1;
|
||||
}
|
||||
.article-editor-loading {
|
||||
padding-top: 150rpx;
|
||||
}
|
||||
.article-editor-content {
|
||||
padding: 18rpx 20rpx calc(38rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
.editor-panel,
|
||||
.editor-result-card {
|
||||
@include adaptive.adaptive-family-panel;
|
||||
width: 100%;
|
||||
}
|
||||
.editor-panel__body {
|
||||
padding: 38rpx 34rpx 42rpx;
|
||||
}
|
||||
.editor-eyebrow {
|
||||
display: block;
|
||||
color: $brand-red;
|
||||
font-size: 23rpx;
|
||||
letter-spacing: 3rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-title {
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
color: $ink;
|
||||
font-family: STKaiti, KaiTi, serif;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-intro {
|
||||
display: block;
|
||||
margin: 10rpx 8rpx 22rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.55;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-field {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
.editor-field__label {
|
||||
display: block;
|
||||
margin: 0 8rpx 8rpx;
|
||||
color: $ink;
|
||||
font-size: 25rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.editor-control {
|
||||
@include adaptive.adaptive-family-field;
|
||||
display: flex;
|
||||
min-height: 82rpx;
|
||||
align-items: center;
|
||||
}
|
||||
.editor-control input,
|
||||
.editor-control textarea {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
color: $ink;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.editor-control input {
|
||||
width: 100%;
|
||||
min-height: 82rpx;
|
||||
padding: 0 28rpx;
|
||||
}
|
||||
.editor-control--textarea {
|
||||
min-height: 220rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.editor-control textarea {
|
||||
width: 100%;
|
||||
min-height: 180rpx;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.editor-control--error {
|
||||
filter: sepia(0.18) saturate(1.2);
|
||||
}
|
||||
.editor-placeholder {
|
||||
color: #9e8e79;
|
||||
}
|
||||
.editor-field__error,
|
||||
.editor-save-error {
|
||||
display: block;
|
||||
color: $brand-red;
|
||||
font-size: 24rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
.editor-field__error {
|
||||
margin: 5rpx 8rpx 0;
|
||||
}
|
||||
.editor-draft-note {
|
||||
display: block;
|
||||
margin: 20rpx 10rpx 0;
|
||||
color: $ink-muted;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.55;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-save-error {
|
||||
margin: 12rpx 8rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-panel__body > .app-button {
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
.editor-result-card {
|
||||
min-height: 420rpx;
|
||||
}
|
||||
.editor-result-card__body {
|
||||
padding: 112rpx 52rpx 68rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.editor-result-card__title {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
color: $ink;
|
||||
font-family: STKaiti, KaiTi, serif;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.editor-result-card__copy {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.editor-result-card__body > .app-button {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
@media (max-width: 340px) {
|
||||
.article-editor-content {
|
||||
padding-right: 14rpx;
|
||||
padding-left: 14rpx;
|
||||
}
|
||||
.editor-panel__body {
|
||||
padding-right: 26rpx;
|
||||
padding-left: 26rpx;
|
||||
}
|
||||
}
|
||||
.article-editor-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
|
||||
.article-editor-page__header, .article-editor-content { z-index: 1; }
|
||||
.article-editor-content { padding: 18rpx 20rpx calc(38rpx + env(safe-area-inset-bottom)); }
|
||||
.editor-panel, .editor-result-card { @include adaptive.adaptive-family-panel; width: 100%; }
|
||||
.editor-panel__body { padding: 38rpx 34rpx 42rpx; }
|
||||
.editor-eyebrow { display: block; color: $brand-red; font-size: 23rpx; letter-spacing: 3rpx; text-align: center; }
|
||||
.editor-title { display: block; margin-top: 10rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 34rpx; font-weight: 700; text-align: center; }
|
||||
.editor-intro { display: block; margin: 10rpx 8rpx 22rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.55; text-align: center; }
|
||||
.editor-field { margin-top: 18rpx; }
|
||||
.editor-field__label { display: block; margin: 0 8rpx 8rpx; color: $ink; font-size: 25rpx; font-weight: 700; }
|
||||
.editor-control { @include adaptive.adaptive-family-field; display: flex; min-height: 82rpx; align-items: center; }
|
||||
.editor-control input, .editor-control textarea { box-sizing: border-box; width: 100%; color: $ink; font-size: 24rpx; }
|
||||
.editor-control input { min-height: 82rpx; padding: 0 28rpx; }
|
||||
.editor-control--textarea { min-height: 220rpx; padding: 20rpx 24rpx; }
|
||||
.editor-control textarea { min-height: 180rpx; line-height: 1.65; }
|
||||
.editor-placeholder { color: #9e8e79; }
|
||||
.editor-save-error { display: block; margin: 12rpx 8rpx 0; color: $brand-red; font-size: 24rpx; line-height: 34rpx; text-align: center; }
|
||||
.editor-panel__body > .app-button { margin-top: 22rpx; }
|
||||
.editor-result-card { min-height: 420rpx; }
|
||||
.editor-result-card__body { padding: 112rpx 52rpx 68rpx; text-align: center; }
|
||||
.editor-result-card__title { display: block; margin-top: 14rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 36rpx; font-weight: 700; }
|
||||
.editor-result-card__copy { display: block; margin-top: 16rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
|
||||
.editor-result-card__body > .app-button { margin-top: 30rpx; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user