minitest2.0 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +8 -0
- package/.env.development +3 -0
- package/.env.production +3 -0
- package/.env.test +3 -0
- package/.gitattributes +1 -0
- package/.oxlintrc.json +10 -0
- package/.prettierrc.json +6 -0
- package/.vscode/extensions.json +9 -0
- package/.vscode/settings.json +13 -0
- package/README.md +179 -0
- package/auto-imports.d.ts +629 -0
- package/components.d.ts +21 -0
- package/design-qa.md +36 -0
- package/docs/MX_API.md +244 -0
- package/docs/REQUEST.md +217 -0
- package/docs/jsapi.js +515 -0
- package/docs/package-json-guide.md +132 -0
- package/env.d.ts +15 -0
- package/eslint.config.ts +26 -0
- package/index.html +16 -0
- package/package.json +83 -0
- package/plugins/bump-version.ts +61 -0
- package/postcss.config.ts +15 -0
- package/public/favicon.ico +0 -0
- package/public/images/12a73787-86a9-4891-a65f-66104746f6a8.png +0 -0
- package/public/images/5798d7aa-ba8b-4605-8079-58b35495ac55.png +0 -0
- package/public/images/73fef1e4-0fd0-4a1a-9b8b-a70a5b6acbbc.png +0 -0
- package/public/images/bc685b4c-0cca-4a79-924c-a8ee10e6f8eb.png +0 -0
- package/public/images/c3dbbd9d-be56-490e-b9f4-6ee17ebefffc.png +0 -0
- package/public/images/ea745a10-42aa-4f44-8d7f-3ab02cc0adcd.png +0 -0
- package/public/images/f5876785-b927-4347-ba19-999114240649.png +0 -0
- package/public/images/img.png +0 -0
- package/public/images/opening-reserve-estimate.png +0 -0
- package/public/images/position-estimate-report.png +0 -0
- package/src/App.vue +131 -0
- package/src/api/announcement.ts +405 -0
- package/src/api/health.ts +13 -0
- package/src/api/pbc-position.ts +178 -0
- package/src/api/rmb-position.ts +233 -0
- package/src/api/tam.ts +173 -0
- package/src/api/user.ts +92 -0
- package/src/auto-imports.d.ts +633 -0
- package/src/components/AppTitleBar.vue +376 -0
- package/src/components.d.ts +33 -0
- package/src/config/config.properties +3 -0
- package/src/config/env.ts +6 -0
- package/src/config/plugin.properties.pro +6 -0
- package/src/config/plugin.properties.test +6 -0
- package/src/core/mxApi/index.ts +451 -0
- package/src/core/request/index.ts +135 -0
- package/src/main.ts +40 -0
- package/src/router/index.ts +144 -0
- package/src/stores/app.ts +103 -0
- package/src/stores/counter.ts +12 -0
- package/src/stores/user.ts +137 -0
- package/src/styles/nprogress.css +22 -0
- package/src/styles/vant-overrides.css +42 -0
- package/src/types/api.d.ts +14 -0
- package/src/types/nprogress.d.ts +8 -0
- package/src/utils/auth-token.ts +241 -0
- package/src/utils/code-highlight.ts +165 -0
- package/src/utils/copy.ts +36 -0
- package/src/utils/query.ts +27 -0
- package/src/utils/request.ts +238 -0
- package/src/utils/rmb-forecast.ts +84 -0
- package/src/utils/toast.ts +61 -0
- package/src/views/article-detail/index.vue +289 -0
- package/src/views/article-edit/index.vue +600 -0
- package/src/views/articles/index.vue +293 -0
- package/src/views/clearing-detail/index.vue +26 -0
- package/src/views/dashboard/index.vue +18 -0
- package/src/views/foreign-position/index.vue +26 -0
- package/src/views/home/index.vue +213 -0
- package/src/views/login/index.vue +275 -0
- package/src/views/mine/index.vue +147 -0
- package/src/views/net-debit/index.vue +26 -0
- package/src/views/opening-reserve-estimate/index.vue +28 -0
- package/src/views/pbc-position/index.vue +357 -0
- package/src/views/position-estimate/index.vue +67 -0
- package/src/views/position-estimate-report/index.vue +28 -0
- package/src/views/rmb-position/index.vue +1013 -0
- package/src/views/rmb-position-create/index.vue +221 -0
- package/src/views/rmb-position-detail/index.vue +355 -0
- package/src/views/settings/index.vue +67 -0
- package/src/views/warning/index.vue +26 -0
- package/tsconfig.app.json +18 -0
- package/tsconfig.json +11 -0
- package/tsconfig.node.json +28 -0
- package/vite.config.ts +100 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
createArticle,
|
|
4
|
+
extractTextFromHtml,
|
|
5
|
+
getArticleDetail,
|
|
6
|
+
updateArticle,
|
|
7
|
+
} from '@/api/announcement'
|
|
8
|
+
import { showAppToast } from '@/utils/toast'
|
|
9
|
+
|
|
10
|
+
defineOptions({ name: 'ArticleEditPage' })
|
|
11
|
+
|
|
12
|
+
const route = useRoute()
|
|
13
|
+
const router = useRouter()
|
|
14
|
+
|
|
15
|
+
const editorRef = ref<HTMLElement>()
|
|
16
|
+
const savedRange = ref<Range | null>(null)
|
|
17
|
+
const title = ref('')
|
|
18
|
+
const contentHtml = ref('')
|
|
19
|
+
const detailLoading = ref(false)
|
|
20
|
+
const saving = ref(false)
|
|
21
|
+
const showLinkPopup = ref(false)
|
|
22
|
+
const showCodePopup = ref(false)
|
|
23
|
+
const linkForm = reactive({
|
|
24
|
+
text: '',
|
|
25
|
+
url: '',
|
|
26
|
+
})
|
|
27
|
+
const codeForm = reactive({
|
|
28
|
+
language: 'javascript',
|
|
29
|
+
code: '',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const articleId = computed(() => String(route.params.id || ''))
|
|
33
|
+
const isEditing = computed(() => Boolean(articleId.value))
|
|
34
|
+
const pageTitle = computed(() => (isEditing.value ? '编辑公告' : '发布公告'))
|
|
35
|
+
|
|
36
|
+
onMounted(async () => {
|
|
37
|
+
if (!isEditing.value) {
|
|
38
|
+
setEditorHtml('')
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
detailLoading.value = true
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const article = await getArticleDetail(articleId.value)
|
|
46
|
+
title.value = article.title
|
|
47
|
+
setEditorHtml(article.content)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('[公告] 编辑详情加载失败', error)
|
|
50
|
+
showAppToast('公告加载失败')
|
|
51
|
+
router.replace('/articles')
|
|
52
|
+
} finally {
|
|
53
|
+
detailLoading.value = false
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
function setEditorHtml(value: string) {
|
|
58
|
+
contentHtml.value = value
|
|
59
|
+
nextTick(() => {
|
|
60
|
+
if (editorRef.value) {
|
|
61
|
+
editorRef.value.innerHTML = value
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function syncEditor() {
|
|
67
|
+
contentHtml.value = editorRef.value?.innerHTML || ''
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function rememberSelection() {
|
|
71
|
+
const selection = window.getSelection()
|
|
72
|
+
|
|
73
|
+
if (!selection || !selection.rangeCount || !editorRef.value) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const anchorNode = selection.anchorNode
|
|
78
|
+
|
|
79
|
+
if (anchorNode && editorRef.value.contains(anchorNode)) {
|
|
80
|
+
savedRange.value = selection.getRangeAt(0).cloneRange()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function restoreSelection() {
|
|
85
|
+
if (!savedRange.value) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const selection = window.getSelection()
|
|
90
|
+
|
|
91
|
+
if (!selection) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
selection.removeAllRanges()
|
|
96
|
+
selection.addRange(savedRange.value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function focusEditor() {
|
|
100
|
+
editorRef.value?.focus()
|
|
101
|
+
restoreSelection()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function runCommand(command: string, value?: string) {
|
|
105
|
+
focusEditor()
|
|
106
|
+
document.execCommand(command, false, value)
|
|
107
|
+
syncEditor()
|
|
108
|
+
rememberSelection()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function setBlock(tag: 'P' | 'H2' | 'H3' | 'BLOCKQUOTE') {
|
|
112
|
+
runCommand('formatBlock', tag)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function clearFormat() {
|
|
116
|
+
runCommand('removeFormat')
|
|
117
|
+
setBlock('P')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function openLinkPopup() {
|
|
121
|
+
rememberSelection()
|
|
122
|
+
linkForm.text = savedRange.value?.toString().trim() || ''
|
|
123
|
+
linkForm.url = ''
|
|
124
|
+
showLinkPopup.value = true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function insertLink() {
|
|
128
|
+
const url = normalizeUrl(linkForm.url)
|
|
129
|
+
|
|
130
|
+
if (!url) {
|
|
131
|
+
showAppToast('请输入链接地址')
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const text = linkForm.text.trim() || url
|
|
136
|
+
focusEditor()
|
|
137
|
+
document.execCommand(
|
|
138
|
+
'insertHTML',
|
|
139
|
+
false,
|
|
140
|
+
`<a href="${escapeAttribute(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(text)}</a>`,
|
|
141
|
+
)
|
|
142
|
+
syncEditor()
|
|
143
|
+
rememberSelection()
|
|
144
|
+
showLinkPopup.value = false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function openCodePopup() {
|
|
148
|
+
rememberSelection()
|
|
149
|
+
codeForm.language = 'javascript'
|
|
150
|
+
codeForm.code = ''
|
|
151
|
+
showCodePopup.value = true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function insertCodeBlock() {
|
|
155
|
+
const code = codeForm.code.trimEnd()
|
|
156
|
+
|
|
157
|
+
if (!code.trim()) {
|
|
158
|
+
showAppToast('请输入代码内容')
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const language = normalizeCodeLanguage(codeForm.language)
|
|
163
|
+
const languageClass = language ? `language-${language}` : 'language-plaintext'
|
|
164
|
+
const languageAttr = language || 'plaintext'
|
|
165
|
+
|
|
166
|
+
focusEditor()
|
|
167
|
+
document.execCommand(
|
|
168
|
+
'insertHTML',
|
|
169
|
+
false,
|
|
170
|
+
`<pre><code class="${languageClass}" data-language="${escapeAttribute(languageAttr)}">${escapeHtml(code)}</code></pre><p><br></p>`,
|
|
171
|
+
)
|
|
172
|
+
syncEditor()
|
|
173
|
+
rememberSelection()
|
|
174
|
+
showCodePopup.value = false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function handleCancel() {
|
|
178
|
+
if (isEditing.value) {
|
|
179
|
+
router.replace(`/articles/${articleId.value}`)
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
router.replace('/articles')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function handleSave() {
|
|
187
|
+
syncEditor()
|
|
188
|
+
|
|
189
|
+
const text = extractTextFromHtml(contentHtml.value)
|
|
190
|
+
|
|
191
|
+
if (!title.value.trim()) {
|
|
192
|
+
showAppToast('请输入公告标题')
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!text) {
|
|
197
|
+
showAppToast('请输入公告正文')
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
saving.value = true
|
|
202
|
+
|
|
203
|
+
const payload = {
|
|
204
|
+
title: title.value,
|
|
205
|
+
content: contentHtml.value,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
const article = isEditing.value
|
|
210
|
+
? await updateArticle(articleId.value, payload)
|
|
211
|
+
: await createArticle(payload)
|
|
212
|
+
|
|
213
|
+
showAppToast(isEditing.value ? '已保存' : '已发布')
|
|
214
|
+
router.replace(article.id ? `/articles/${article.id}` : '/articles')
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('[公告] 保存失败', error)
|
|
217
|
+
} finally {
|
|
218
|
+
saving.value = false
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function normalizeUrl(value: string) {
|
|
223
|
+
const url = value.trim()
|
|
224
|
+
|
|
225
|
+
if (!url) {
|
|
226
|
+
return ''
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (/^(https?:\/\/|mailto:|tel:|#)/i.test(url)) {
|
|
230
|
+
return url
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return `https://${url}`
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function escapeHtml(value: string) {
|
|
237
|
+
const div = document.createElement('div')
|
|
238
|
+
div.textContent = value
|
|
239
|
+
|
|
240
|
+
return div.innerHTML
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function escapeAttribute(value: string) {
|
|
244
|
+
return escapeHtml(value).replace(/"/g, '"')
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function normalizeCodeLanguage(value: string) {
|
|
248
|
+
return value
|
|
249
|
+
.trim()
|
|
250
|
+
.toLowerCase()
|
|
251
|
+
.replace(/[^a-z0-9_-]/g, '')
|
|
252
|
+
.slice(0, 24)
|
|
253
|
+
}
|
|
254
|
+
</script>
|
|
255
|
+
|
|
256
|
+
<template>
|
|
257
|
+
<main class="article-edit-page">
|
|
258
|
+
<section class="form-panel">
|
|
259
|
+
<van-field
|
|
260
|
+
v-model="title"
|
|
261
|
+
label="标题"
|
|
262
|
+
placeholder="请输入公告标题"
|
|
263
|
+
clearable
|
|
264
|
+
maxlength="40"
|
|
265
|
+
show-word-limit
|
|
266
|
+
/>
|
|
267
|
+
</section>
|
|
268
|
+
|
|
269
|
+
<section v-if="detailLoading" class="state-section">
|
|
270
|
+
<van-loading vertical>加载中...</van-loading>
|
|
271
|
+
</section>
|
|
272
|
+
|
|
273
|
+
<section v-else class="editor-panel">
|
|
274
|
+
<div class="editor-title">
|
|
275
|
+
<h2>正文</h2>
|
|
276
|
+
<span>富文本编辑</span>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div class="editor-toolbar" aria-label="富文本工具栏">
|
|
280
|
+
<button type="button" title="正文" @click="setBlock('P')">P</button>
|
|
281
|
+
<button type="button" title="二级标题" @click="setBlock('H2')">H2</button>
|
|
282
|
+
<button type="button" title="三级标题" @click="setBlock('H3')">H3</button>
|
|
283
|
+
<button type="button" title="加粗" @click="runCommand('bold')"><strong>B</strong></button>
|
|
284
|
+
<button type="button" title="斜体" @click="runCommand('italic')"><em>I</em></button>
|
|
285
|
+
<button type="button" title="下划线" @click="runCommand('underline')"><u>U</u></button>
|
|
286
|
+
<button type="button" title="引用" @click="setBlock('BLOCKQUOTE')">
|
|
287
|
+
<span aria-hidden="true">“</span>
|
|
288
|
+
</button>
|
|
289
|
+
<button type="button" title="无序列表" @click="runCommand('insertUnorderedList')">
|
|
290
|
+
<van-icon name="orders-o" />
|
|
291
|
+
</button>
|
|
292
|
+
<button type="button" title="有序列表" @click="runCommand('insertOrderedList')">1.</button>
|
|
293
|
+
<button type="button" title="插入链接" @click="openLinkPopup">
|
|
294
|
+
<van-icon name="link-o" />
|
|
295
|
+
</button>
|
|
296
|
+
<button type="button" title="插入代码块" @click="openCodePopup">
|
|
297
|
+
<van-icon name="font-o" />
|
|
298
|
+
</button>
|
|
299
|
+
<button type="button" title="清除格式" @click="clearFormat">
|
|
300
|
+
<van-icon name="close" />
|
|
301
|
+
</button>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div
|
|
305
|
+
ref="editorRef"
|
|
306
|
+
class="rich-editor"
|
|
307
|
+
contenteditable="true"
|
|
308
|
+
data-placeholder="请输入公告正文"
|
|
309
|
+
@input="syncEditor"
|
|
310
|
+
@mouseup="rememberSelection"
|
|
311
|
+
@keyup="rememberSelection"
|
|
312
|
+
@blur="rememberSelection"
|
|
313
|
+
></div>
|
|
314
|
+
</section>
|
|
315
|
+
|
|
316
|
+
<footer class="edit-actions">
|
|
317
|
+
<van-button block plain type="default" @click="handleCancel">取消</van-button>
|
|
318
|
+
<van-button
|
|
319
|
+
block
|
|
320
|
+
type="danger"
|
|
321
|
+
:loading="saving"
|
|
322
|
+
:disabled="detailLoading"
|
|
323
|
+
@click="handleSave"
|
|
324
|
+
>
|
|
325
|
+
{{ isEditing ? '保存公告' : '发布公告' }}
|
|
326
|
+
</van-button>
|
|
327
|
+
</footer>
|
|
328
|
+
|
|
329
|
+
<van-popup v-model:show="showLinkPopup" round position="bottom">
|
|
330
|
+
<div class="link-popup">
|
|
331
|
+
<header>
|
|
332
|
+
<h2>插入链接</h2>
|
|
333
|
+
<button type="button" aria-label="关闭" @click="showLinkPopup = false">
|
|
334
|
+
<van-icon name="cross" />
|
|
335
|
+
</button>
|
|
336
|
+
</header>
|
|
337
|
+
<van-cell-group inset>
|
|
338
|
+
<van-field v-model="linkForm.text" label="文字" placeholder="链接显示文字" />
|
|
339
|
+
<van-field v-model="linkForm.url" label="链接" placeholder="https://example.com" />
|
|
340
|
+
</van-cell-group>
|
|
341
|
+
<div class="link-actions">
|
|
342
|
+
<van-button block plain @click="showLinkPopup = false">取消</van-button>
|
|
343
|
+
<van-button block type="danger" @click="insertLink">插入</van-button>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</van-popup>
|
|
347
|
+
|
|
348
|
+
<van-popup v-model:show="showCodePopup" round position="bottom">
|
|
349
|
+
<div class="code-popup">
|
|
350
|
+
<header>
|
|
351
|
+
<h2>插入代码块</h2>
|
|
352
|
+
<button type="button" aria-label="关闭" @click="showCodePopup = false">
|
|
353
|
+
<van-icon name="cross" />
|
|
354
|
+
</button>
|
|
355
|
+
</header>
|
|
356
|
+
<van-cell-group inset>
|
|
357
|
+
<van-field v-model="codeForm.language" label="语言" placeholder="javascript" />
|
|
358
|
+
<van-field
|
|
359
|
+
v-model="codeForm.code"
|
|
360
|
+
class="code-field"
|
|
361
|
+
label="代码"
|
|
362
|
+
type="textarea"
|
|
363
|
+
rows="8"
|
|
364
|
+
autosize
|
|
365
|
+
placeholder="请输入代码"
|
|
366
|
+
/>
|
|
367
|
+
</van-cell-group>
|
|
368
|
+
<div class="code-actions">
|
|
369
|
+
<van-button block plain @click="showCodePopup = false">取消</van-button>
|
|
370
|
+
<van-button block type="danger" @click="insertCodeBlock">插入</van-button>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
</van-popup>
|
|
374
|
+
</main>
|
|
375
|
+
</template>
|
|
376
|
+
|
|
377
|
+
<style scoped>
|
|
378
|
+
.article-edit-page {
|
|
379
|
+
width: 100%;
|
|
380
|
+
max-width: 750px;
|
|
381
|
+
min-height: var(--app-page-min-height, 100vh);
|
|
382
|
+
margin: 0 auto;
|
|
383
|
+
padding: 0 11px 24px;
|
|
384
|
+
color: #171b20;
|
|
385
|
+
background: #f7f8fa;
|
|
386
|
+
box-sizing: border-box;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.form-panel,
|
|
390
|
+
.editor-panel {
|
|
391
|
+
overflow: hidden;
|
|
392
|
+
border: 1px solid #edf0f4;
|
|
393
|
+
border-radius: 8px;
|
|
394
|
+
background: #ffffff;
|
|
395
|
+
box-shadow: 0 5px 16px rgba(23, 27, 32, 0.04);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.form-panel {
|
|
399
|
+
margin-top: 12px;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.form-panel :deep(.van-cell) {
|
|
403
|
+
--van-cell-vertical-padding: 14px;
|
|
404
|
+
--van-field-label-width: 44px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.editor-panel {
|
|
408
|
+
margin-top: 12px;
|
|
409
|
+
padding: 14px;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.state-section {
|
|
413
|
+
display: flex;
|
|
414
|
+
justify-content: center;
|
|
415
|
+
padding: 110px 0;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.editor-title {
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: center;
|
|
421
|
+
justify-content: space-between;
|
|
422
|
+
gap: 10px;
|
|
423
|
+
margin-bottom: 12px;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.editor-title h2 {
|
|
427
|
+
margin: 0;
|
|
428
|
+
color: #171b20;
|
|
429
|
+
font-size: 16px;
|
|
430
|
+
font-weight: 750;
|
|
431
|
+
line-height: 1.2;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.editor-title span {
|
|
435
|
+
color: #8a929f;
|
|
436
|
+
font-size: 12px;
|
|
437
|
+
line-height: 1.2;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.editor-toolbar {
|
|
441
|
+
display: flex;
|
|
442
|
+
flex-wrap: wrap;
|
|
443
|
+
gap: 7px;
|
|
444
|
+
padding-bottom: 12px;
|
|
445
|
+
border-bottom: 1px solid #f0f1f3;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.editor-toolbar button {
|
|
449
|
+
display: inline-flex;
|
|
450
|
+
align-items: center;
|
|
451
|
+
justify-content: center;
|
|
452
|
+
min-width: 32px;
|
|
453
|
+
height: 32px;
|
|
454
|
+
padding: 0 8px;
|
|
455
|
+
border: 1px solid #e8ebf0;
|
|
456
|
+
border-radius: 6px;
|
|
457
|
+
color: #252a31;
|
|
458
|
+
font-size: 13px;
|
|
459
|
+
font-weight: 700;
|
|
460
|
+
line-height: 1;
|
|
461
|
+
background: #fbfcfd;
|
|
462
|
+
cursor: pointer;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.editor-toolbar button:active {
|
|
466
|
+
color: #f22f3d;
|
|
467
|
+
border-color: #ffd1d6;
|
|
468
|
+
background: #fff4f5;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.rich-editor {
|
|
472
|
+
height: clamp(300px, 52vh, 430px);
|
|
473
|
+
max-height: clamp(300px, 52vh, 430px);
|
|
474
|
+
overflow-y: auto;
|
|
475
|
+
overscroll-behavior: contain;
|
|
476
|
+
padding: 14px 2px 18px 0;
|
|
477
|
+
color: #252a31;
|
|
478
|
+
font-size: 16px;
|
|
479
|
+
line-height: 1.75;
|
|
480
|
+
outline: none;
|
|
481
|
+
word-break: break-word;
|
|
482
|
+
box-sizing: border-box;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.rich-editor:empty::before {
|
|
486
|
+
content: attr(data-placeholder);
|
|
487
|
+
color: #b6bdc8;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.rich-editor :deep(h2),
|
|
491
|
+
.rich-editor :deep(h3),
|
|
492
|
+
.rich-editor :deep(p),
|
|
493
|
+
.rich-editor :deep(blockquote),
|
|
494
|
+
.rich-editor :deep(ul),
|
|
495
|
+
.rich-editor :deep(ol) {
|
|
496
|
+
margin: 0 0 12px;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.rich-editor :deep(h2) {
|
|
500
|
+
font-size: 20px;
|
|
501
|
+
line-height: 1.4;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.rich-editor :deep(h3) {
|
|
505
|
+
font-size: 18px;
|
|
506
|
+
line-height: 1.4;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.rich-editor :deep(blockquote) {
|
|
510
|
+
padding: 10px 12px;
|
|
511
|
+
border-left: 4px solid #d8dde6;
|
|
512
|
+
border-radius: 6px;
|
|
513
|
+
color: #596273;
|
|
514
|
+
background: #f6f7f9;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.rich-editor :deep(pre) {
|
|
518
|
+
overflow-x: auto;
|
|
519
|
+
margin: 0 0 12px;
|
|
520
|
+
padding: 12px;
|
|
521
|
+
border: 1px solid #e4e8ef;
|
|
522
|
+
border-radius: 7px;
|
|
523
|
+
background: #101820;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.rich-editor :deep(code) {
|
|
527
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
|
|
528
|
+
font-size: 13px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.rich-editor :deep(pre code) {
|
|
532
|
+
display: block;
|
|
533
|
+
color: #eef3f7;
|
|
534
|
+
line-height: 1.62;
|
|
535
|
+
white-space: pre;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.edit-actions {
|
|
539
|
+
display: grid;
|
|
540
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
541
|
+
gap: 10px;
|
|
542
|
+
margin-top: 16px;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.edit-actions :deep(.van-button) {
|
|
546
|
+
height: 42px;
|
|
547
|
+
font-weight: 600;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.link-popup,
|
|
551
|
+
.code-popup {
|
|
552
|
+
padding: 18px 0 max(18px, env(safe-area-inset-bottom));
|
|
553
|
+
background: #ffffff;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.link-popup header,
|
|
557
|
+
.code-popup header {
|
|
558
|
+
display: flex;
|
|
559
|
+
align-items: center;
|
|
560
|
+
justify-content: space-between;
|
|
561
|
+
padding: 0 16px 14px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.link-popup h2,
|
|
565
|
+
.code-popup h2 {
|
|
566
|
+
margin: 0;
|
|
567
|
+
color: #171b20;
|
|
568
|
+
font-size: 18px;
|
|
569
|
+
font-weight: 750;
|
|
570
|
+
line-height: 1.2;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.link-popup header button,
|
|
574
|
+
.code-popup header button {
|
|
575
|
+
display: flex;
|
|
576
|
+
align-items: center;
|
|
577
|
+
justify-content: center;
|
|
578
|
+
width: 32px;
|
|
579
|
+
height: 32px;
|
|
580
|
+
border: 0;
|
|
581
|
+
border-radius: 50%;
|
|
582
|
+
color: #6b7280;
|
|
583
|
+
font-size: 18px;
|
|
584
|
+
background: #f5f6f8;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.link-actions,
|
|
588
|
+
.code-actions {
|
|
589
|
+
display: grid;
|
|
590
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
591
|
+
gap: 10px;
|
|
592
|
+
padding: 16px 16px 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.code-field :deep(textarea) {
|
|
596
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
|
|
597
|
+
font-size: 13px;
|
|
598
|
+
line-height: 1.55;
|
|
599
|
+
}
|
|
600
|
+
</style>
|