mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-06 00:01:33 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aec9ea83c5 | ||
|
|
effd13aedd | ||
|
|
42b43d65d7 | ||
|
|
c501d824dd | ||
|
|
384ac2faf1 | ||
|
|
dd2c4dd24b | ||
|
|
356ffddb1c | ||
|
|
de69be7c4e | ||
|
|
e962f555ae | ||
|
|
1987246585 | ||
|
|
393264f66b | ||
|
|
9b50020b3b | ||
|
|
5e5545fe01 | ||
|
|
0e8da35b0a | ||
|
|
4d2cf73330 | ||
|
|
5df89f2ce4 | ||
|
|
045c0b4c0c | ||
|
|
8b4ffa0795 | ||
|
|
14359a37ae | ||
|
|
a8e4a1c2e0 | ||
|
|
9048d181af | ||
|
|
db9b2ee6b3 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "1.9.8-2",
|
"version": "1.9.13",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": "dist/service.js",
|
"bin": "dist/service.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -101,4 +101,4 @@
|
|||||||
"resolutions": {
|
"resolutions": {
|
||||||
"postcss": "8"
|
"postcss": "8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ import modeYamlUrl from 'ace-builds/src-noconflict/mode-yaml?url'
|
|||||||
|
|
||||||
import modeCssUrl from 'ace-builds/src-noconflict/mode-css?url'
|
import modeCssUrl from 'ace-builds/src-noconflict/mode-css?url'
|
||||||
|
|
||||||
|
import modePythonUrl from 'ace-builds/src-noconflict/mode-python?url'
|
||||||
|
|
||||||
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url'
|
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url'
|
||||||
|
|
||||||
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url'
|
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url'
|
||||||
@@ -38,6 +40,8 @@ import snippetsJsonUrl from 'ace-builds/src-noconflict/snippets/json?url'
|
|||||||
|
|
||||||
import snippertsCssUrl from 'ace-builds/src-noconflict/snippets/css?url'
|
import snippertsCssUrl from 'ace-builds/src-noconflict/snippets/css?url'
|
||||||
|
|
||||||
|
import snippetsPythonUrl from 'ace-builds/src-noconflict/snippets/python?url'
|
||||||
|
|
||||||
import 'ace-builds/src-noconflict/ext-language_tools'
|
import 'ace-builds/src-noconflict/ext-language_tools'
|
||||||
|
|
||||||
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl)
|
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl)
|
||||||
@@ -45,6 +49,7 @@ ace.config.setModuleUrl('ace/mode/javascript', modeJavascriptUrl)
|
|||||||
ace.config.setModuleUrl('ace/mode/html', modeHtmlUrl)
|
ace.config.setModuleUrl('ace/mode/html', modeHtmlUrl)
|
||||||
ace.config.setModuleUrl('ace/mode/yaml', modeYamlUrl)
|
ace.config.setModuleUrl('ace/mode/yaml', modeYamlUrl)
|
||||||
ace.config.setModuleUrl('ace/mode/css', modeCssUrl)
|
ace.config.setModuleUrl('ace/mode/css', modeCssUrl)
|
||||||
|
ace.config.setModuleUrl('ace/mode/python', modePythonUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl)
|
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl)
|
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl)
|
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl)
|
||||||
@@ -59,5 +64,6 @@ ace.config.setModuleUrl('ace/snippets/javascript', snippetsJsUrl)
|
|||||||
ace.config.setModuleUrl('ace/snippets/javascript', snippetsYamlUrl)
|
ace.config.setModuleUrl('ace/snippets/javascript', snippetsYamlUrl)
|
||||||
ace.config.setModuleUrl('ace/snippets/json', snippetsJsonUrl)
|
ace.config.setModuleUrl('ace/snippets/json', snippetsJsonUrl)
|
||||||
ace.config.setModuleUrl('ace/snippets/css', snippertsCssUrl)
|
ace.config.setModuleUrl('ace/snippets/css', snippertsCssUrl)
|
||||||
|
ace.config.setModuleUrl('ace/snippets/python', snippetsPythonUrl)
|
||||||
|
|
||||||
ace.require('ace/ext/language_tools')
|
ace.require('ace/ext/language_tools')
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ function filtersChanged(value: string[]) {
|
|||||||
emit('changed', props.pri, value)
|
emit('changed', props.pri, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清洗规则中的换行符和多余空格,并在前后添加空格
|
||||||
|
const cleanedRules = computed(() => {
|
||||||
|
return props.rules.map(rule => {
|
||||||
|
rule = rule ?? ''
|
||||||
|
return ` ${rule.replace(/[\r\n]/g, '').replace(/\s+/g, '')} `
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// 过滤规则下拉框
|
// 过滤规则下拉框
|
||||||
const selectFilterOptions = ref<{ [key: string]: string }[]>([
|
const selectFilterOptions = ref<{ [key: string]: string }[]>([
|
||||||
{ title: '特效字幕', value: ' SPECSUB ' },
|
{ title: '特效字幕', value: ' SPECSUB ' },
|
||||||
@@ -77,7 +85,7 @@ const selectFilterOptions = ref<{ [key: string]: string }[]>([
|
|||||||
<VRow>
|
<VRow>
|
||||||
<VCol>
|
<VCol>
|
||||||
<VSelect
|
<VSelect
|
||||||
v-model="props.rules"
|
v-model="cleanedRules"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
:items="selectFilterOptions"
|
:items="selectFilterOptions"
|
||||||
chips
|
chips
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ watch(
|
|||||||
'outline-dashed outline-1': props.media?.best_version && imageLoaded,
|
'outline-dashed outline-1': props.media?.best_version && imageLoaded,
|
||||||
'transition transform-cpu duration-300 scale-105 shadow-lg': hover.isHovering,
|
'transition transform-cpu duration-300 scale-105 shadow-lg': hover.isHovering,
|
||||||
}"
|
}"
|
||||||
|
min-height="170"
|
||||||
@click="editSubscribeDialog"
|
@click="editSubscribeDialog"
|
||||||
>
|
>
|
||||||
<div class="me-n3 absolute top-1 right-2">
|
<div class="me-n3 absolute top-1 right-2">
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const transferItems = ref<FileItem[]>([])
|
|||||||
|
|
||||||
// 大小控制
|
// 大小控制
|
||||||
const scrollStyle = computed(() => {
|
const scrollStyle = computed(() => {
|
||||||
return appMode
|
return appMode.value
|
||||||
? 'height: calc(100vh - 15.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
? 'height: calc(100vh - 15.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
||||||
: 'height: calc(100vh - 14.5rem - env(safe-area-inset-bottom)'
|
: 'height: calc(100vh - 14.5rem - env(safe-area-inset-bottom)'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import * as Mousetrap from 'mousetrap'
|
import * as Mousetrap from 'mousetrap'
|
||||||
import SearchBarView from '@/views/system/SearchBarView.vue'
|
import SearchBarView from '@/views/system/SearchBarView.vue'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
|
|
||||||
@@ -15,6 +16,14 @@ function openSearchDialog() {
|
|||||||
searchDialog.value = true
|
searchDialog.value = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测操作系统是否是Mac
|
||||||
|
function isMac() {
|
||||||
|
return navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算属性:根据操作系统显示不同的按键提示
|
||||||
|
const metaKey = computed(() => (isMac() ? '⌘+K' : 'Ctrl+K'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -25,12 +34,13 @@ function openSearchDialog() {
|
|||||||
</IconBtn>
|
</IconBtn>
|
||||||
<span v-if="display.lgAndUp.value" class="flex align-center text-disabled ms-2" @click="openSearchDialog">
|
<span v-if="display.lgAndUp.value" class="flex align-center text-disabled ms-2" @click="openSearchDialog">
|
||||||
<span class="me-3">搜索</span>
|
<span class="me-3">搜索</span>
|
||||||
<span class="meta-key">⌘K</span>
|
<span class="meta-key">{{ metaKey }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 搜索弹窗 -->
|
<!-- 搜索弹窗 -->
|
||||||
<SearchBarView v-model="searchDialog" v-if="searchDialog" @close="searchDialog = false" />
|
<SearchBarView v-model="searchDialog" v-if="searchDialog" @close="searchDialog = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style type="scss" scoped>
|
<style type="scss" scoped>
|
||||||
.meta-key {
|
.meta-key {
|
||||||
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
|
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||||
@@ -39,4 +49,4 @@ function openSearchDialog() {
|
|||||||
padding-block: 0.1rem;
|
padding-block: 0.1rem;
|
||||||
padding-inline: 0.25rem;
|
padding-inline: 0.25rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -80,7 +80,13 @@ const options = controlledComputed(
|
|||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
},
|
},
|
||||||
|
|
||||||
formatter: (value: number) => (value > 999 ? (value / 1000).toFixed(0) : value),
|
formatter: (value: number) => {
|
||||||
|
if (value > 999) {
|
||||||
|
return (value / 1000).toFixed(1) + 'k'
|
||||||
|
} else {
|
||||||
|
return value.toString()
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Context } from '@/api/types'
|
import type { Context } from '@/api/types'
|
||||||
import TorrentItem from '@/components/cards/TorrentItem.vue'
|
import TorrentItem from '@/components/cards/TorrentItem.vue'
|
||||||
|
import { list } from 'postcss'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
|
|
||||||
// 显示器宽度
|
// 显示器宽度
|
||||||
@@ -35,6 +36,13 @@ const filterForm = reactive({
|
|||||||
resolution: [] as string[],
|
resolution: [] as string[],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 列表样式
|
||||||
|
const listStyle = computed(() => {
|
||||||
|
return appMode.value
|
||||||
|
? 'height: calc(100vh - 7.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
||||||
|
: 'height: calc(100vh - 6.5rem - env(safe-area-inset-bottom)'
|
||||||
|
})
|
||||||
|
|
||||||
// 排序字段
|
// 排序字段
|
||||||
const sortField = ref('default')
|
const sortField = ref('default')
|
||||||
|
|
||||||
@@ -167,14 +175,7 @@ onMounted(() => {
|
|||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
<VList v-if="dataList.length !== 0" lines="three" class="rounded p-0 torrent-list-vscroll shadow-lg">
|
<VList v-if="dataList.length !== 0" lines="three" class="rounded p-0 torrent-list-vscroll shadow-lg">
|
||||||
<VVirtualScroll
|
<VVirtualScroll :items="dataList" :style="listStyle">
|
||||||
:items="dataList"
|
|
||||||
:style="
|
|
||||||
appMode
|
|
||||||
? 'height: calc(100vh - 7.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
|
||||||
: 'height: calc(100vh - 6.5rem - env(safe-area-inset-bottom)'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<TorrentItem :torrent="item" :key="`${item.torrent_info.page_url}`" />
|
<TorrentItem :torrent="item" :key="`${item.torrent_info.page_url}`" />
|
||||||
</template>
|
</template>
|
||||||
@@ -182,11 +183,7 @@ onMounted(() => {
|
|||||||
</VList>
|
</VList>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol xl="2" md="3" v-if="display.mdAndUp.value">
|
<VCol xl="2" md="3" v-if="display.mdAndUp.value">
|
||||||
<VList
|
<VList lines="one" class="rounded shadow-lg" :style="listStyle">
|
||||||
lines="one"
|
|
||||||
class="rounded shadow-lg"
|
|
||||||
style="block-size: calc(100vh - 6.5rem - env(safe-area-inset-bottom))"
|
|
||||||
>
|
|
||||||
<VListSubheader> 排序 </VListSubheader>
|
<VListSubheader> 排序 </VListSubheader>
|
||||||
<VListItem>
|
<VListItem>
|
||||||
<VChipGroup column v-model="sortField">
|
<VChipGroup column v-model="sortField">
|
||||||
|
|||||||
@@ -124,6 +124,12 @@ const TransferDict: { [key: string]: string } = {
|
|||||||
rclone_move: 'Rclone移动',
|
rclone_move: 'Rclone移动',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableStyle = computed(() => {
|
||||||
|
return appMode.value
|
||||||
|
? 'height: calc(100vh - 15.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
||||||
|
: 'height: calc(100vh - 14.5rem - env(safe-area-inset-bottom)'
|
||||||
|
})
|
||||||
|
|
||||||
// 分页提示
|
// 分页提示
|
||||||
const pageTip = computed(() => {
|
const pageTip = computed(() => {
|
||||||
const begin = itemsPerPage.value * (currentPage.value - 1) + 1
|
const begin = itemsPerPage.value * (currentPage.value - 1) + 1
|
||||||
@@ -142,12 +148,19 @@ const totalPage = computed(() => {
|
|||||||
|
|
||||||
// 切换页签和搜索词
|
// 切换页签和搜索词
|
||||||
watch(
|
watch(
|
||||||
[() => currentPage.value, () => itemsPerPage.value, () => search.value],
|
[() => currentPage.value, () => itemsPerPage.value],
|
||||||
debounce(async () => {
|
debounce(async () => {
|
||||||
reloadPage()
|
reloadPage()
|
||||||
}, 1000),
|
}, 1000),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[() => search.value],
|
||||||
|
debounce(async () => {
|
||||||
|
reloadPage(true)
|
||||||
|
}, 1000),
|
||||||
|
)
|
||||||
|
|
||||||
// 获取订阅列表数据
|
// 获取订阅列表数据
|
||||||
async function fetchData(page = currentPage.value, count = itemsPerPage.value) {
|
async function fetchData(page = currentPage.value, count = itemsPerPage.value) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -329,7 +342,7 @@ function addUrlQuery(url: string, name: string, value: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重载页面
|
// 重载页面
|
||||||
function reloadPage() {
|
function reloadPage(resetPage = false) {
|
||||||
let url = '/history'
|
let url = '/history'
|
||||||
if (search.value) {
|
if (search.value) {
|
||||||
url = addUrlQuery(url, 'search', search.value)
|
url = addUrlQuery(url, 'search', search.value)
|
||||||
@@ -338,7 +351,7 @@ function reloadPage() {
|
|||||||
url = addUrlQuery(url, 'itemsPerPage', itemsPerPage.value)
|
url = addUrlQuery(url, 'itemsPerPage', itemsPerPage.value)
|
||||||
}
|
}
|
||||||
if (currentPage.value) {
|
if (currentPage.value) {
|
||||||
url = addUrlQuery(url, 'currentPage', currentPage.value)
|
url = addUrlQuery(url, 'currentPage', resetPage ? 1 : currentPage.value)
|
||||||
}
|
}
|
||||||
router.push(url)
|
router.push(url)
|
||||||
}
|
}
|
||||||
@@ -394,11 +407,7 @@ onMounted(fetchData)
|
|||||||
show-select
|
show-select
|
||||||
loading-text="加载中..."
|
loading-text="加载中..."
|
||||||
hover
|
hover
|
||||||
:style="
|
:style="tableStyle"
|
||||||
appMode
|
|
||||||
? 'height: calc(100vh - 15.5rem - env(safe-area-inset-bottom) - 3.5rem)'
|
|
||||||
: 'height: calc(100vh - 14.5rem - env(safe-area-inset-bottom)'
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<template #item.title="{ item }">
|
<template #item.title="{ item }">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import type { Plugin, Subscribe } from '@/api/types'
|
import type { Plugin, Subscribe } from '@/api/types'
|
||||||
import { SystemNavMenus, UserfulMenus, PluginTabs, SettingTabs } from '@/router/menu'
|
import { SystemNavMenus, UserfulMenus, SettingTabs } from '@/router/menu'
|
||||||
import { NavMenu } from '@/@layouts/types'
|
import { NavMenu } from '@/@layouts/types'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
// 路由
|
// 路由
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 超级用户
|
||||||
|
const superUser = store.state.auth.superUser
|
||||||
|
|
||||||
|
// 当前用户名
|
||||||
|
const userName = store.state.auth.userName
|
||||||
|
|
||||||
// 定义事件
|
// 定义事件
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
@@ -74,6 +81,7 @@ function getMenus(): NavMenu[] {
|
|||||||
// 匹配的菜单列表
|
// 匹配的菜单列表
|
||||||
const matchedMenuItems = computed(() => {
|
const matchedMenuItems = computed(() => {
|
||||||
if (!searchWord.value) return []
|
if (!searchWord.value) return []
|
||||||
|
if (!superUser) return []
|
||||||
const lowerWord = (searchWord.value as string).toLowerCase()
|
const lowerWord = (searchWord.value as string).toLowerCase()
|
||||||
const menuItems = getMenus()
|
const menuItems = getMenus()
|
||||||
if (menuItems)
|
if (menuItems)
|
||||||
@@ -104,6 +112,7 @@ async function fetchInstalledPlugins() {
|
|||||||
// 区配的插件列表
|
// 区配的插件列表
|
||||||
const matchedPluginItems = computed(() => {
|
const matchedPluginItems = computed(() => {
|
||||||
if (!searchWord.value) return []
|
if (!searchWord.value) return []
|
||||||
|
if (!superUser) return []
|
||||||
const lowerWord = (searchWord.value as string).toLowerCase()
|
const lowerWord = (searchWord.value as string).toLowerCase()
|
||||||
return pluginItems.value.filter((item: Plugin) => {
|
return pluginItems.value.filter((item: Plugin) => {
|
||||||
if (!item.plugin_name && !item.plugin_desc) return false
|
if (!item.plugin_name && !item.plugin_desc) return false
|
||||||
@@ -114,7 +123,7 @@ const matchedPluginItems = computed(() => {
|
|||||||
// 所有订阅数据
|
// 所有订阅数据
|
||||||
const SubscribeItems = ref<Subscribe[]>([])
|
const SubscribeItems = ref<Subscribe[]>([])
|
||||||
|
|
||||||
// 获取电影订阅列表数据
|
// 获取订阅列表数据
|
||||||
async function fetchSubscribes() {
|
async function fetchSubscribes() {
|
||||||
try {
|
try {
|
||||||
SubscribeItems.value = await api.get('subscribe/')
|
SubscribeItems.value = await api.get('subscribe/')
|
||||||
@@ -128,7 +137,7 @@ const matchedSubscribeItems = computed(() => {
|
|||||||
if (!searchWord.value) return []
|
if (!searchWord.value) return []
|
||||||
const lowerWord = (searchWord.value as string).toLowerCase()
|
const lowerWord = (searchWord.value as string).toLowerCase()
|
||||||
return SubscribeItems.value.filter((item: Subscribe) => {
|
return SubscribeItems.value.filter((item: Subscribe) => {
|
||||||
return item.name.toLowerCase().includes(lowerWord)
|
return (item.name.toLowerCase().includes(lowerWord) && (superUser || userName === item.username)) || false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -287,7 +296,7 @@ onMounted(() => {
|
|||||||
</VListItem>
|
</VListItem>
|
||||||
</template>
|
</template>
|
||||||
</VHover>
|
</VHover>
|
||||||
<VHover>
|
<VHover v-if="superUser">
|
||||||
<template #default="hover">
|
<template #default="hover">
|
||||||
<VListItem prepend-icon="mdi-history" link v-bind="hover.props" @click="searchHistory">
|
<VListItem prepend-icon="mdi-history" link v-bind="hover.props" @click="searchHistory">
|
||||||
<VListItemTitle class="break-words whitespace-break-spaces">
|
<VListItemTitle class="break-words whitespace-break-spaces">
|
||||||
@@ -382,7 +391,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
<VRow>
|
<VRow v-if="superUser">
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<p class="custom-letter-spacing text-sm text-disabled text-uppercase py-2 px-4 mb-0">常用功能</p>
|
<p class="custom-letter-spacing text-sm text-disabled text-uppercase py-2 px-4 mb-0">常用功能</p>
|
||||||
<VList lines="one">
|
<VList lines="one">
|
||||||
|
|||||||
Reference in New Issue
Block a user