优化指南菜单页面样式
This commit is contained in:
@@ -10,6 +10,13 @@
|
|||||||
"filePath": "File Path",
|
"filePath": "File Path",
|
||||||
"updateTime": "Updated At"
|
"updateTime": "Updated At"
|
||||||
},
|
},
|
||||||
|
"catalog": {
|
||||||
|
"title": "Catalog",
|
||||||
|
"empty": "No headings"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"zoom": "Click to view full size"
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"loadFailed": "Failed to load admin guide",
|
"loadFailed": "Failed to load admin guide",
|
||||||
"saveSuccess": "Saved successfully",
|
"saveSuccess": "Saved successfully",
|
||||||
|
|||||||
@@ -10,6 +10,13 @@
|
|||||||
"filePath": "文档路径",
|
"filePath": "文档路径",
|
||||||
"updateTime": "更新时间"
|
"updateTime": "更新时间"
|
||||||
},
|
},
|
||||||
|
"catalog": {
|
||||||
|
"title": "目录",
|
||||||
|
"empty": "暂无目录"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"zoom": "点击查看原图"
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"loadFailed": "加载操作指南失败",
|
"loadFailed": "加载操作指南失败",
|
||||||
"saveSuccess": "保存成功",
|
"saveSuccess": "保存成功",
|
||||||
|
|||||||
@@ -54,29 +54,73 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-loading="loading" class="admin-guide-body flex-1 min-h-0 overflow-auto">
|
<div v-loading="loading" class="admin-guide-body flex-1 min-h-0">
|
||||||
<SaMdEditor
|
<SaMdEditor
|
||||||
v-if="isEditing"
|
v-if="isEditing"
|
||||||
v-model="editContent"
|
v-model="editContent"
|
||||||
|
class="admin-guide-editor"
|
||||||
height="calc(100vh - 220px)"
|
height="calc(100vh - 220px)"
|
||||||
min-height="480px"
|
min-height="480px"
|
||||||
/>
|
/>
|
||||||
|
<div v-else class="admin-guide-read flex h-full min-h-0">
|
||||||
|
<aside class="admin-guide-catalog flex-shrink-0">
|
||||||
|
<div class="catalog-title">{{ $t('page.catalog.title') }}</div>
|
||||||
|
<ElScrollbar class="catalog-scroll">
|
||||||
|
<nav v-if="tocList.length" class="guide-toc">
|
||||||
|
<button
|
||||||
|
v-for="(item, index) in tocList"
|
||||||
|
:key="`${item.level}-${item.text}-${index}`"
|
||||||
|
type="button"
|
||||||
|
class="guide-toc-item"
|
||||||
|
:class="[
|
||||||
|
`guide-toc-level-${item.level}`,
|
||||||
|
{ 'is-active': activeTocIndex === index }
|
||||||
|
]"
|
||||||
|
:title="item.text"
|
||||||
|
@click="scrollToHeading(item, index)"
|
||||||
|
>
|
||||||
|
{{ item.text }}
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<div v-else class="guide-toc-empty">{{ $t('page.catalog.empty') }}</div>
|
||||||
|
</ElScrollbar>
|
||||||
|
</aside>
|
||||||
|
<div
|
||||||
|
id="admin-guide-scroll"
|
||||||
|
ref="previewScrollRef"
|
||||||
|
class="admin-guide-preview-wrap flex-1 min-h-0 overflow-auto"
|
||||||
|
>
|
||||||
<MdPreview
|
<MdPreview
|
||||||
v-else
|
:editor-id="previewEditorId"
|
||||||
:model-value="previewContent"
|
:model-value="previewContent"
|
||||||
:theme="previewTheme"
|
:theme="previewTheme"
|
||||||
preview-theme="github"
|
preview-theme="github"
|
||||||
|
no-img-zoom-in
|
||||||
class="admin-guide-preview"
|
class="admin-guide-preview"
|
||||||
|
@on-html-changed="handlePreviewHtmlReady"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
|
|
||||||
|
<ElImageViewer
|
||||||
|
v-if="imageViewerVisible"
|
||||||
|
:key="imageViewerIndex"
|
||||||
|
:url-list="previewImageUrls"
|
||||||
|
:initial-index="imageViewerIndex"
|
||||||
|
:hide-on-click-modal="true"
|
||||||
|
:z-index="3000"
|
||||||
|
teleported
|
||||||
|
@close="closeImageViewer"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElImageViewer, ElMessageBox } from 'element-plus'
|
||||||
import { MdPreview } from 'md-editor-v3'
|
import { MdPreview } from 'md-editor-v3'
|
||||||
import 'md-editor-v3/lib/preview.css'
|
import 'md-editor-v3/lib/preview.css'
|
||||||
import SaMdEditor from '@/components/sai/sa-md-editor/index.vue'
|
import SaMdEditor from '@/components/sai/sa-md-editor/index.vue'
|
||||||
@@ -85,9 +129,16 @@
|
|||||||
|
|
||||||
defineOptions({ name: 'SystemAdminGuide' })
|
defineOptions({ name: 'SystemAdminGuide' })
|
||||||
|
|
||||||
|
interface GuideTocItem {
|
||||||
|
level: number
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
|
|
||||||
|
const previewEditorId = 'admin-guide-preview'
|
||||||
|
const previewScrollRef = ref<HTMLElement | null>(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const isEditing = ref(false)
|
const isEditing = ref(false)
|
||||||
@@ -95,10 +146,17 @@
|
|||||||
const editContent = ref('')
|
const editContent = ref('')
|
||||||
const originalContent = ref('')
|
const originalContent = ref('')
|
||||||
const meta = ref<{ filePath?: string; updateTime?: string }>({})
|
const meta = ref<{ filePath?: string; updateTime?: string }>({})
|
||||||
|
const tocList = ref<GuideTocItem[]>([])
|
||||||
|
const activeTocIndex = ref(-1)
|
||||||
|
const previewImageUrls = ref<string[]>([])
|
||||||
|
const imageViewerVisible = ref(false)
|
||||||
|
const imageViewerIndex = ref(0)
|
||||||
|
|
||||||
|
let previewImageClickHandler: ((event: Event) => void) | null = null
|
||||||
|
let previewEnhanceTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
const previewTheme = computed(() => (settingStore.isDark ? 'dark' : 'light'))
|
const previewTheme = computed(() => (settingStore.isDark ? 'dark' : 'light'))
|
||||||
|
|
||||||
/** 静态资源根地址(public 目录,开发环境走代理地址) */
|
|
||||||
const getStaticFileBase = (): string => {
|
const getStaticFileBase = (): string => {
|
||||||
const apiUrl = import.meta.env.VITE_API_URL || ''
|
const apiUrl = import.meta.env.VITE_API_URL || ''
|
||||||
if (apiUrl.startsWith('http')) {
|
if (apiUrl.startsWith('http')) {
|
||||||
@@ -121,19 +179,159 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseTocFromMarkdown = (content: string): GuideTocItem[] => {
|
||||||
|
const items: GuideTocItem[] = []
|
||||||
|
for (const line of content.split('\n')) {
|
||||||
|
const match = line.match(/^(#{1,6})\s+(.+)$/)
|
||||||
|
if (!match) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
level: match[1].length,
|
||||||
|
text: match[2].trim()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshTocList = () => {
|
||||||
|
tocList.value = parseTocFromMarkdown(originalContent.value)
|
||||||
|
activeTocIndex.value = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const collectPreviewImages = () => {
|
||||||
|
const container = previewScrollRef.value
|
||||||
|
if (!container) {
|
||||||
|
previewImageUrls.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const imgs = container.querySelectorAll('img')
|
||||||
|
previewImageUrls.value = Array.from(imgs).map((img) => {
|
||||||
|
const el = img as HTMLImageElement
|
||||||
|
return el.currentSrc || el.src
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const findImageIndex = (img: HTMLImageElement): number => {
|
||||||
|
collectPreviewImages()
|
||||||
|
const targetSrc = img.currentSrc || img.src
|
||||||
|
let index = previewImageUrls.value.indexOf(targetSrc)
|
||||||
|
if (index >= 0) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
index = previewImageUrls.value.findIndex((url) => targetSrc.endsWith(url) || url.endsWith(targetSrc))
|
||||||
|
if (index >= 0) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
previewImageUrls.value = [...previewImageUrls.value, targetSrc]
|
||||||
|
return previewImageUrls.value.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImageViewerByImg = (img: HTMLImageElement) => {
|
||||||
|
const index = findImageIndex(img)
|
||||||
|
openImageViewer(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImageViewer = (index: number) => {
|
||||||
|
if (index < 0 || index >= previewImageUrls.value.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imageViewerIndex.value = index
|
||||||
|
imageViewerVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeImageViewer = () => {
|
||||||
|
imageViewerVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const unbindPreviewImageClick = (clearTimer = true) => {
|
||||||
|
const container = previewScrollRef.value
|
||||||
|
if (container && previewImageClickHandler) {
|
||||||
|
container.removeEventListener('click', previewImageClickHandler, true)
|
||||||
|
}
|
||||||
|
previewImageClickHandler = null
|
||||||
|
if (clearTimer && previewEnhanceTimer) {
|
||||||
|
clearTimeout(previewEnhanceTimer)
|
||||||
|
previewEnhanceTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bindPreviewImageClick = () => {
|
||||||
|
unbindPreviewImageClick(false)
|
||||||
|
const container = previewScrollRef.value
|
||||||
|
if (!container) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
collectPreviewImages()
|
||||||
|
previewImageClickHandler = (event: Event) => {
|
||||||
|
const imgEl = (event.target as Element | null)?.closest('img')
|
||||||
|
if (!(imgEl instanceof HTMLImageElement)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!container.contains(imgEl)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
openImageViewerByImg(imgEl)
|
||||||
|
}
|
||||||
|
container.addEventListener('click', previewImageClickHandler, true)
|
||||||
|
|
||||||
|
const imgs = container.querySelectorAll('img')
|
||||||
|
imgs.forEach((img) => {
|
||||||
|
const el = img as HTMLImageElement
|
||||||
|
el.style.cursor = 'zoom-in'
|
||||||
|
el.title = t('page.image.zoom')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreviewHtmlReady = () => {
|
||||||
|
if (previewEnhanceTimer) {
|
||||||
|
clearTimeout(previewEnhanceTimer)
|
||||||
|
}
|
||||||
|
previewEnhanceTimer = setTimeout(() => {
|
||||||
|
bindPreviewImageClick()
|
||||||
|
previewEnhanceTimer = null
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupPreviewEnhancements = async () => {
|
||||||
|
await nextTick()
|
||||||
|
refreshTocList()
|
||||||
|
handlePreviewHtmlReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToHeading = (item: GuideTocItem, index: number) => {
|
||||||
|
const container = previewScrollRef.value
|
||||||
|
if (!container) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6')
|
||||||
|
for (const heading of Array.from(headings)) {
|
||||||
|
if (heading.textContent?.trim() === item.text) {
|
||||||
|
activeTocIndex.value = index
|
||||||
|
heading.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadContent = async () => {
|
const loadContent = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
unbindPreviewImageClick()
|
||||||
try {
|
try {
|
||||||
const res = await api.read()
|
const res = await api.read()
|
||||||
const data = res as { content?: string; file_path?: string; update_time?: string }
|
const data = res as { content?: string; file_path?: string; update_time?: string }
|
||||||
const rawContent = data.content ?? ''
|
const rawContent = data.content ?? ''
|
||||||
previewContent.value = resolveGuideImages(rawContent)
|
|
||||||
editContent.value = rawContent
|
editContent.value = rawContent
|
||||||
originalContent.value = rawContent
|
originalContent.value = rawContent
|
||||||
|
previewContent.value = resolveGuideImages(rawContent)
|
||||||
meta.value = {
|
meta.value = {
|
||||||
filePath: data.file_path,
|
filePath: data.file_path,
|
||||||
updateTime: data.update_time
|
updateTime: data.update_time
|
||||||
}
|
}
|
||||||
|
await setupPreviewEnhancements()
|
||||||
} catch {
|
} catch {
|
||||||
// 错误由 http 工具统一处理
|
// 错误由 http 工具统一处理
|
||||||
} finally {
|
} finally {
|
||||||
@@ -142,9 +340,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startEdit = () => {
|
const startEdit = () => {
|
||||||
editContent.value = previewContent.value
|
editContent.value = originalContent.value
|
||||||
originalContent.value = previewContent.value
|
|
||||||
isEditing.value = true
|
isEditing.value = true
|
||||||
|
unbindPreviewImageClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = async () => {
|
const handleCancel = async () => {
|
||||||
@@ -159,6 +357,7 @@
|
|||||||
}
|
}
|
||||||
editContent.value = originalContent.value
|
editContent.value = originalContent.value
|
||||||
isEditing.value = false
|
isEditing.value = false
|
||||||
|
await setupPreviewEnhancements()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
@@ -178,6 +377,7 @@
|
|||||||
updateTime: data.update_time ?? meta.value.updateTime
|
updateTime: data.update_time ?? meta.value.updateTime
|
||||||
}
|
}
|
||||||
isEditing.value = false
|
isEditing.value = false
|
||||||
|
await setupPreviewEnhancements()
|
||||||
} catch {
|
} catch {
|
||||||
// 错误由 http 工具统一处理
|
// 错误由 http 工具统一处理
|
||||||
} finally {
|
} finally {
|
||||||
@@ -185,9 +385,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(previewContent, async () => {
|
||||||
|
if (!isEditing.value) {
|
||||||
|
await nextTick()
|
||||||
|
handlePreviewHtmlReady()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadContent()
|
loadContent()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
unbindPreviewImageClick()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -203,9 +414,136 @@
|
|||||||
|
|
||||||
.admin-guide-body {
|
.admin-guide-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-guide-read {
|
||||||
|
min-height: 0;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-guide-catalog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 240px;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 4px 12px 12px 4px;
|
||||||
|
border-right: 1px solid var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-title {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-scroll {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
text-align: left;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: color 0.2s, background-color 0.2s;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.is-active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-level-1 {
|
||||||
|
padding-left: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-level-2 {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-level-3 {
|
||||||
|
padding-left: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-level-4 {
|
||||||
|
padding-left: 44px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-level-5,
|
||||||
|
.guide-toc-level-6 {
|
||||||
|
padding-left: 56px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-toc-empty {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-guide-preview-wrap {
|
||||||
|
position: relative;
|
||||||
|
padding: 8px 12px 8px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-guide-preview {
|
.admin-guide-preview {
|
||||||
padding: 8px 4px;
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-guide-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.admin-guide-read {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-guide-catalog {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 200px;
|
||||||
|
padding-right: 0;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.admin-guide-preview-wrap .md-editor-preview-wrapper img,
|
||||||
|
.admin-guide-preview-wrap .md-editor-preview img,
|
||||||
|
.admin-guide-editor .md-editor-preview-wrapper img,
|
||||||
|
.admin-guide-editor .md-editor-preview img {
|
||||||
|
display: block;
|
||||||
|
width: 50% !important;
|
||||||
|
max-width: 50% !important;
|
||||||
|
height: auto !important;
|
||||||
|
margin: 8px 0;
|
||||||
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
## 菜单简单介绍
|
## 菜单简单介绍
|
||||||
|
|
||||||
## 工作台/统计页面:统计数据
|
### 工作台/统计页面:统计数据
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 角色管理:对角色的菜单权限设置
|
### 角色管理:对角色的菜单权限设置
|
||||||
|
|
||||||
按等级设定,等级越低权限越少(不要出现上级角色没有的权限,子角色有)
|
按等级设定,等级越低权限越少(不要出现上级角色没有的权限,子角色有)
|
||||||
避免方式:使用子角色创建下级角色,可以避免下级角色比上级角色操作权限更多的问题
|
避免方式:使用子角色创建下级角色,可以避免下级角色比上级角色操作权限更多的问题
|
||||||
@@ -17,13 +17,13 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 彩金池配置:监听彩金池实时变化
|
### 彩金池配置:监听彩金池实时变化
|
||||||
|
|
||||||
可以实时监听彩金池累积金额的变化
|
可以实时监听彩金池累积金额的变化
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 游戏配置:游戏规则和平台币转化比
|
### 游戏配置:游戏规则和平台币转化比
|
||||||
|
|
||||||
游戏配置
|
游戏配置
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
游戏平台币兑换币:为进入平台时平台比转化比,比如,当前设置的为1:1,如果从jk8平台转入100,那么获取的游戏币为100,如果设置1:2则获取的平台币为200
|
游戏平台币兑换币:为进入平台时平台比转化比,比如,当前设置的为1:1,如果从jk8平台转入100,那么获取的游戏币为100,如果设置1:2则获取的平台币为200
|
||||||
|
|
||||||
## 底注配置:方便玩家快速调整压注倍率
|
### 底注配置:方便玩家快速调整压注倍率
|
||||||
|
|
||||||
底注配置
|
底注配置
|
||||||
|
|
||||||
@@ -45,9 +45,9 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
# 抽奖逻辑
|
## 抽奖逻辑
|
||||||
|
|
||||||
## 判断抽奖档位
|
### 判断抽奖档位
|
||||||
|
|
||||||
当前的抽奖逻辑时,按照抽奖档位(T1-T5)进行抽奖,在【玩家管理】菜单中的设置玩家具体的档位权重
|
当前的抽奖逻辑时,按照抽奖档位(T1-T5)进行抽奖,在【玩家管理】菜单中的设置玩家具体的档位权重
|
||||||
|
|
||||||
@@ -69,9 +69,9 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 根据档位抽取中奖号码
|
### 根据档位抽取中奖号码
|
||||||
|
|
||||||
### 设置中奖号码地图
|
#### 设置中奖号码地图
|
||||||
|
|
||||||
在后台设置地图缩影
|
在后台设置地图缩影
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 创建完地图索引后创建相应的奖励对照表
|
#### 创建完地图索引后创建相应的奖励对照表
|
||||||
|
|
||||||
创建奖励对照表的原因是由于有每个号码的权重不一样,豹子号10,15,20,25有多重组合方式,所以需要设置奖励对照表中的权重配比
|
创建奖励对照表的原因是由于有每个号码的权重不一样,豹子号10,15,20,25有多重组合方式,所以需要设置奖励对照表中的权重配比
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 扩展
|
#### 扩展
|
||||||
|
|
||||||
- 测试设置的中奖概率,根据如下设置可以测试当前设置权重的中奖概率,该测试数据不记录到真实数据系统中
|
- 测试设置的中奖概率,根据如下设置可以测试当前设置权重的中奖概率,该测试数据不记录到真实数据系统中
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user