mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-31 20:19:46 +08:00
802 lines
20 KiB
Vue
802 lines
20 KiB
Vue
<template>
|
|
<div id="plugin-view">
|
|
<div class="view-title">
|
|
{{ $T('PLUGIN_SETTINGS') }} -
|
|
<el-tooltip
|
|
:content="pluginListToolTip"
|
|
placement="right"
|
|
:persistent="false"
|
|
teleported
|
|
>
|
|
<el-icon
|
|
class="el-icon-goods"
|
|
@click="goAwesomeList"
|
|
>
|
|
<Goods />
|
|
</el-icon>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
:content="updateAllToolTip"
|
|
placement="left"
|
|
:persistent="false"
|
|
teleported
|
|
>
|
|
<el-icon
|
|
class="el-icon-update"
|
|
@click="handleUpdateAllPlugin"
|
|
>
|
|
<Refresh />
|
|
</el-icon>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
:content="importLocalPluginToolTip"
|
|
placement="left"
|
|
>
|
|
<el-icon
|
|
class="el-icon-download"
|
|
:persistent="false"
|
|
teleported
|
|
@click="handleImportLocalPlugin"
|
|
>
|
|
<Download />
|
|
</el-icon>
|
|
</el-tooltip>
|
|
</div>
|
|
<el-row
|
|
class="handle-bar"
|
|
:class="{ 'cut-width': pluginList.length > 6 }"
|
|
>
|
|
<el-input
|
|
v-model="searchText"
|
|
:placeholder="$T('PLUGIN_SEARCH_PLACEHOLDER')"
|
|
size="small"
|
|
>
|
|
<template #suffix>
|
|
<el-icon
|
|
class="el-input__icon"
|
|
style="cursor: pointer;"
|
|
@click="cleanSearch"
|
|
>
|
|
<close />
|
|
</el-icon>
|
|
</template>
|
|
</el-input>
|
|
</el-row>
|
|
<el-row
|
|
id="pluginList"
|
|
v-loading="loading"
|
|
:gutter="10"
|
|
class="plugin-list"
|
|
>
|
|
<el-col
|
|
v-for="item in pluginList"
|
|
:key="item.fullName"
|
|
class="plugin-item__container"
|
|
:xs="24"
|
|
:sm="pluginList.length === 1 ? 24 : 12"
|
|
:md="pluginList.length === 1 ? 24 : 12"
|
|
:lg="pluginList.length === 1 ? 24 : 12"
|
|
:xl="pluginList.length === 1 ? 24 : 12"
|
|
>
|
|
<div
|
|
class="plugin-item"
|
|
:class="{ 'darwin': os === 'darwin' }"
|
|
>
|
|
<div
|
|
v-if="!item.gui"
|
|
class="cli-only-badge"
|
|
title="CLI only"
|
|
>
|
|
CLI
|
|
</div>
|
|
<img
|
|
class="plugin-item__logo"
|
|
:src="item.logo"
|
|
:onerror="defaultLogo"
|
|
>
|
|
<div
|
|
class="plugin-item__content"
|
|
:class="{ disabled: !item.enabled }"
|
|
>
|
|
<div
|
|
class="plugin-item__name"
|
|
@click="openHomepage(item.homepage)"
|
|
>
|
|
{{ item.name }} <small>{{ ' ' + item.version }}</small>
|
|
<!-- 升级提示 -->
|
|
<el-tag
|
|
v-if="latestVersionMap[item.fullName] && latestVersionMap[item.fullName] !== item.version"
|
|
type="success"
|
|
size="small"
|
|
round
|
|
effect="plain"
|
|
>
|
|
new
|
|
</el-tag>
|
|
</div>
|
|
<div
|
|
class="plugin-item__desc"
|
|
:title="item.description"
|
|
>
|
|
{{ item.description }}
|
|
</div>
|
|
<div class="plugin-item__info-bar">
|
|
<span class="plugin-item__author">
|
|
{{ item.author.replace(/<.*>/, '') }}
|
|
</span>
|
|
<span class="plugin-item__config">
|
|
<template v-if="searchText">
|
|
<template v-if="!item.hasInstall">
|
|
<span
|
|
v-if="!item.ing"
|
|
class="config-button install"
|
|
@click="installPlugin(item)"
|
|
>
|
|
{{ $T('PLUGIN_INSTALL') }}
|
|
</span>
|
|
<span
|
|
v-else-if="item.ing"
|
|
class="config-button ing"
|
|
>
|
|
{{ $T('PLUGIN_INSTALLING') }}
|
|
</span>
|
|
</template>
|
|
<span
|
|
v-else
|
|
class="config-button ing"
|
|
>
|
|
{{ $T('PLUGIN_INSTALLED') }}
|
|
</span>
|
|
</template>
|
|
<template v-else>
|
|
<span
|
|
v-if="item.ing"
|
|
class="config-button ing"
|
|
>
|
|
{{ $T('PLUGIN_DOING_SOMETHING') }}
|
|
</span>
|
|
<template v-else>
|
|
<el-icon
|
|
v-if="item.enabled"
|
|
class="el-icon-setting"
|
|
@click="buildContextMenu(item)"
|
|
>
|
|
<Tools />
|
|
</el-icon>
|
|
<el-icon
|
|
v-else
|
|
class="el-icon-remove-outline"
|
|
@click="buildContextMenu(item)"
|
|
>
|
|
<Remove />
|
|
</el-icon>
|
|
</template>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
<el-row
|
|
v-show="needReload"
|
|
class="reload-mask"
|
|
:class="{ 'cut-width': pluginList.length > 6 }"
|
|
justify="center"
|
|
>
|
|
<el-button
|
|
type="primary"
|
|
size="small"
|
|
round
|
|
@click="reloadApp"
|
|
>
|
|
{{ $T('TIPS_NEED_RELOAD') }}
|
|
</el-button>
|
|
</el-row>
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:modal-append-to-body="false"
|
|
:title="$T('CONFIG_THING', {
|
|
c: configName
|
|
})"
|
|
width="70%"
|
|
append-to-body
|
|
>
|
|
<config-form
|
|
:id="configName"
|
|
ref="$configForm"
|
|
:config="config"
|
|
:type="currentType"
|
|
color-mode="white"
|
|
/>
|
|
<template #footer>
|
|
<el-button
|
|
round
|
|
@click="dialogVisible = false"
|
|
>
|
|
{{ $T('CANCEL') }}
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
round
|
|
@click="handleConfirmConfig"
|
|
>
|
|
{{ $T('CONFIRM') }}
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
<script lang="ts" setup>
|
|
// Element Plus 图标
|
|
import { Close, Download, Refresh, Goods, Remove, Tools } from '@element-plus/icons-vue'
|
|
|
|
// 国际化函数
|
|
import { T as $T } from '@/i18n/index'
|
|
|
|
// 组件
|
|
import ConfigForm from '@/components/ConfigFormForPlugin.vue'
|
|
|
|
// Lodash 函数节流
|
|
import { debounce, DebouncedFunc } from 'lodash'
|
|
|
|
// Electron 相关
|
|
import {
|
|
ipcRenderer,
|
|
IpcRendererEvent
|
|
} from 'electron'
|
|
|
|
// 工具函数
|
|
import { handleStreamlinePluginName } from '~/universal/utils/common'
|
|
|
|
// 事件常量
|
|
import {
|
|
OPEN_URL,
|
|
PICGO_CONFIG_PLUGIN,
|
|
PICGO_HANDLE_PLUGIN_ING,
|
|
PICGO_TOGGLE_PLUGIN,
|
|
SHOW_PLUGIN_PAGE_MENU,
|
|
GET_PICBEDS,
|
|
PICGO_HANDLE_PLUGIN_DONE
|
|
} from '#/events/constants'
|
|
|
|
// Vue 相关
|
|
import { computed, ref, onBeforeMount, onBeforeUnmount, watch, onMounted, reactive, toRaw } from 'vue'
|
|
|
|
// 数据发送工具函数
|
|
import { getConfig, saveConfig, sendRPC, sendToMain } from '@/utils/dataSender'
|
|
|
|
// Element Plus 消息框组件
|
|
import { ElMessageBox } from 'element-plus'
|
|
|
|
// Axios
|
|
import axios from 'axios'
|
|
|
|
// 枚举类型声明
|
|
import { IRPCActionType } from '~/universal/types/enum'
|
|
|
|
const $confirm = ElMessageBox.confirm
|
|
const searchText = ref('')
|
|
const pluginList = ref<IPicGoPlugin[]>([])
|
|
const config = ref<any[]>([])
|
|
const currentType = ref<'plugin' | 'uploader' | 'transformer'>('plugin')
|
|
const configName = ref('')
|
|
const dialogVisible = ref(false)
|
|
const pluginNameList = ref<string[]>([])
|
|
const loading = ref(true)
|
|
const needReload = ref(false)
|
|
const latestVersionMap = reactive<{ [key: string]: string }>({})
|
|
const pluginListToolTip = $T('PLUGIN_LIST')
|
|
const importLocalPluginToolTip = $T('PLUGIN_IMPORT_LOCAL')
|
|
const updateAllToolTip = $T('PLUGIN_UPDATE_ALL')
|
|
// const id = ref('')
|
|
const os = ref('')
|
|
const defaultLogo = ref(`this.src="file://${__static.replace(/\\/g, '/')}/roundLogo.png"`)
|
|
const $configForm = ref<InstanceType<typeof ConfigForm> | null>(null)
|
|
const npmSearchText = computed(() => {
|
|
return searchText.value.match('picgo-plugin-')
|
|
? searchText.value
|
|
: searchText.value !== ''
|
|
? `picgo-plugin-${searchText.value}`
|
|
: searchText.value
|
|
})
|
|
let getSearchResult: DebouncedFunc<(val: string) => void>
|
|
|
|
watch(npmSearchText, (val: string) => {
|
|
if (val) {
|
|
loading.value = true
|
|
pluginList.value = []
|
|
getSearchResult(val)
|
|
} else {
|
|
getPluginList()
|
|
}
|
|
})
|
|
|
|
watch(dialogVisible, (val: boolean) => {
|
|
if (val) {
|
|
// @ts-ignore
|
|
document.querySelector('.main-content.el-row').style.zIndex = 101
|
|
} else {
|
|
// @ts-ignore
|
|
document.querySelector('.main-content.el-row').style.zIndex = 10
|
|
}
|
|
})
|
|
|
|
async function getLatestVersionOfPlugIn (pluginName: string) {
|
|
try {
|
|
const res = await axios.get(`https://registry.npmjs.com/${pluginName}`)
|
|
latestVersionMap[pluginName] = res.data['dist-tags'].latest
|
|
} catch (err) {
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
onBeforeMount(async () => {
|
|
os.value = process.platform
|
|
ipcRenderer.on('hideLoading', () => {
|
|
loading.value = false
|
|
})
|
|
ipcRenderer.on(PICGO_HANDLE_PLUGIN_DONE, (evt: IpcRendererEvent, fullName: string) => {
|
|
pluginList.value.forEach(item => {
|
|
if (item.fullName === fullName || (item.name === fullName)) {
|
|
item.ing = false
|
|
}
|
|
})
|
|
loading.value = false
|
|
})
|
|
ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
|
|
pluginList.value = list
|
|
pluginNameList.value = list.map(item => item.fullName)
|
|
for (const item of pluginList.value) {
|
|
getLatestVersionOfPlugIn(item.fullName)
|
|
}
|
|
loading.value = false
|
|
})
|
|
ipcRenderer.on('installPlugin', (evt: IpcRendererEvent, { success, body }: {
|
|
success: boolean,
|
|
body: string
|
|
}) => {
|
|
loading.value = false
|
|
pluginList.value.forEach(item => {
|
|
if (item.fullName === body) {
|
|
item.ing = false
|
|
item.hasInstall = success
|
|
}
|
|
})
|
|
})
|
|
ipcRenderer.on('updateSuccess', (evt: IpcRendererEvent, plugin: string) => {
|
|
loading.value = false
|
|
pluginList.value.forEach(item => {
|
|
if (item.fullName === plugin) {
|
|
item.ing = false
|
|
item.hasInstall = true
|
|
}
|
|
getPicBeds()
|
|
})
|
|
handleReload()
|
|
getPluginList()
|
|
})
|
|
ipcRenderer.on('uninstallSuccess', (evt: IpcRendererEvent, plugin: string) => {
|
|
loading.value = false
|
|
pluginList.value = pluginList.value.filter(item => {
|
|
if (item.fullName === plugin) { // restore Uploader & Transformer after uninstalling
|
|
if (item.config.transformer.name) {
|
|
handleRestoreState('transformer', item.config.transformer.name)
|
|
}
|
|
if (item.config.uploader.name) {
|
|
handleRestoreState('uploader', item.config.uploader.name)
|
|
}
|
|
getPicBeds()
|
|
}
|
|
return item.fullName !== plugin
|
|
})
|
|
pluginNameList.value = pluginNameList.value.filter(item => item !== plugin)
|
|
})
|
|
ipcRenderer.on(PICGO_CONFIG_PLUGIN, (evt: IpcRendererEvent, _currentType: 'plugin' | 'transformer' | 'uploader', _configName: string, _config: any) => {
|
|
currentType.value = _currentType
|
|
configName.value = _configName
|
|
config.value = _config
|
|
dialogVisible.value = true
|
|
})
|
|
ipcRenderer.on(PICGO_HANDLE_PLUGIN_ING, (evt: IpcRendererEvent, fullName: string) => {
|
|
pluginList.value.forEach(item => {
|
|
if (item.fullName === fullName || (item.name === fullName)) {
|
|
item.ing = true
|
|
}
|
|
})
|
|
loading.value = true
|
|
})
|
|
ipcRenderer.on(PICGO_TOGGLE_PLUGIN, (evt: IpcRendererEvent, fullName: string, enabled: boolean) => {
|
|
const plugin = pluginList.value.find(item => item.fullName === fullName)
|
|
if (plugin) {
|
|
plugin.enabled = enabled
|
|
getPicBeds()
|
|
needReload.value = true
|
|
}
|
|
})
|
|
getPluginList()
|
|
getSearchResult = debounce(_getSearchResult, 50)
|
|
needReload.value = await getConfig<boolean>('needReload') || false
|
|
})
|
|
|
|
async function buildContextMenu (plugin: IPicGoPlugin) {
|
|
sendToMain(SHOW_PLUGIN_PAGE_MENU, plugin)
|
|
}
|
|
|
|
function handleResize () {
|
|
const myDiv = document.getElementById('pluginList') as HTMLElement
|
|
const windowHeight = window.innerHeight
|
|
const newHeight = windowHeight * 0.75
|
|
myDiv.style.height = newHeight + 'px'
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('resize', handleResize)
|
|
})
|
|
|
|
function getPluginList () {
|
|
sendToMain('getPluginList')
|
|
}
|
|
|
|
function getPicBeds () {
|
|
sendToMain(GET_PICBEDS)
|
|
}
|
|
|
|
function installPlugin (item: IPicGoPlugin) {
|
|
if (!item.gui) {
|
|
$confirm($T('TIPS_PLUGIN_NOT_GUI_IMPLEMENT'), $T('TIPS_NOTICE'), {
|
|
confirmButtonText: $T('CONFIRM'),
|
|
cancelButtonText: $T('CANCEL'),
|
|
type: 'warning'
|
|
}).then(() => {
|
|
item.ing = true
|
|
sendToMain('installPlugin', item.fullName)
|
|
}).catch(() => {
|
|
console.log('Install canceled')
|
|
})
|
|
} else {
|
|
item.ing = true
|
|
sendToMain('installPlugin', item.fullName)
|
|
}
|
|
}
|
|
|
|
// function uninstallPlugin (val: string) {
|
|
// pluginList.value.forEach(item => {
|
|
// if (item.name === val) {
|
|
// item.ing = true
|
|
// }
|
|
// })
|
|
// loading.value = true
|
|
// sendToMain('uninstallPlugin', val)
|
|
// }
|
|
|
|
// function updatePlugin (val: string) {
|
|
// pluginList.value.forEach(item => {
|
|
// if (item.fullName === val) {
|
|
// item.ing = true
|
|
// }
|
|
// })
|
|
// loading.value = true
|
|
// sendToMain('updatePlugin', val)
|
|
// }
|
|
|
|
function reloadApp () {
|
|
sendRPC(IRPCActionType.RELOAD_APP)
|
|
}
|
|
|
|
async function handleReload () {
|
|
saveConfig({
|
|
needReload: true
|
|
})
|
|
needReload.value = true
|
|
const successNotification = new Notification($T('PLUGIN_UPDATE_SUCCEED'), {
|
|
body: $T('TIPS_NEED_RELOAD')
|
|
})
|
|
successNotification.onclick = () => {
|
|
reloadApp()
|
|
}
|
|
}
|
|
|
|
function cleanSearch () {
|
|
searchText.value = ''
|
|
}
|
|
|
|
async function handleConfirmConfig () {
|
|
const result = (await $configForm.value?.validate() || false)
|
|
if (result !== false) {
|
|
switch (currentType.value) {
|
|
case 'plugin':
|
|
saveConfig({
|
|
[`${configName.value}`]: result
|
|
})
|
|
break
|
|
case 'uploader':
|
|
saveConfig({
|
|
[`picBed.${configName.value}`]: result
|
|
})
|
|
break
|
|
case 'transformer':
|
|
saveConfig({
|
|
[`transformer.${configName.value}`]: result
|
|
})
|
|
break
|
|
}
|
|
const successNotification = new Notification($T('SETTINGS_RESULT'), {
|
|
body: $T('TIPS_SET_SUCCEED')
|
|
})
|
|
successNotification.onclick = () => {
|
|
return true
|
|
}
|
|
dialogVisible.value = false
|
|
getPluginList()
|
|
}
|
|
}
|
|
|
|
function _getSearchResult (val: string) {
|
|
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
|
|
axios.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
|
|
.then((res: INPMSearchResult) => {
|
|
pluginList.value = res.data.objects
|
|
.filter((item:INPMSearchResultObject) => {
|
|
return item.package.name.includes('picgo-plugin-')
|
|
})
|
|
.map((item: INPMSearchResultObject) => {
|
|
return handleSearchResult(item)
|
|
})
|
|
loading.value = false
|
|
})
|
|
.catch(err => {
|
|
console.log(err)
|
|
loading.value = false
|
|
})
|
|
}
|
|
|
|
function handleSearchResult (item: INPMSearchResultObject) {
|
|
const name = handleStreamlinePluginName(item.package.name)
|
|
let gui = false
|
|
if (item.package.keywords && item.package.keywords.length > 0) {
|
|
if (item.package.keywords.includes('picgo-gui-plugin')) {
|
|
gui = true
|
|
}
|
|
}
|
|
return {
|
|
name,
|
|
fullName: item.package.name,
|
|
author: item.package.author.name,
|
|
description: item.package.description,
|
|
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
|
|
config: {},
|
|
homepage: item.package.links ? item.package.links.homepage : '',
|
|
hasInstall: pluginNameList.value.some(plugin => plugin === item.package.name),
|
|
version: item.package.version,
|
|
gui,
|
|
ing: false // installing or uninstalling
|
|
}
|
|
}
|
|
|
|
// restore Uploader & Transformer
|
|
async function handleRestoreState (item: string, name: string) {
|
|
if (item === 'uploader') {
|
|
const current = await getConfig('picBed.current')
|
|
if (current === name) {
|
|
saveConfig({
|
|
'picBed.current': 'smms',
|
|
'picBed.uploader': 'smms'
|
|
})
|
|
}
|
|
}
|
|
if (item === 'transformer') {
|
|
const current = await getConfig('picBed.transformer')
|
|
if (current === name) {
|
|
saveConfig({
|
|
'picBed.transformer': 'path'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
function openHomepage (url: string) {
|
|
if (url) {
|
|
sendToMain(OPEN_URL, url)
|
|
}
|
|
}
|
|
|
|
function goAwesomeList () {
|
|
sendToMain(OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
|
|
}
|
|
|
|
function handleImportLocalPlugin () {
|
|
sendToMain('importLocalPlugin')
|
|
loading.value = true
|
|
}
|
|
|
|
function handleUpdateAllPlugin () {
|
|
sendToMain('updateAllPlugin', toRaw(pluginNameList.value))
|
|
}
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('resize', handleResize)
|
|
ipcRenderer.removeAllListeners('pluginList')
|
|
ipcRenderer.removeAllListeners('installPlugin')
|
|
ipcRenderer.removeAllListeners('uninstallSuccess')
|
|
ipcRenderer.removeAllListeners('updateSuccess')
|
|
ipcRenderer.removeAllListeners('hideLoading')
|
|
ipcRenderer.removeAllListeners(PICGO_HANDLE_PLUGIN_DONE)
|
|
})
|
|
|
|
</script>
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'PluginPage'
|
|
}
|
|
</script>
|
|
<style lang='stylus'>
|
|
$darwinBg = #172426
|
|
#plugin-view
|
|
position absolute
|
|
left 142px
|
|
right 0
|
|
.el-loading-mask
|
|
background-color rgba(0, 0, 0, 0.8)
|
|
.plugin-list
|
|
align-content flex-start
|
|
height: 600px;
|
|
box-sizing: border-box;
|
|
padding: 8px 15px;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
position: absolute;
|
|
top: 70px;
|
|
left: 5px;
|
|
transition: all 0.2s ease-in-out 0.1s;
|
|
width: 100%
|
|
.el-loading-mask
|
|
left: 20px
|
|
width: calc(100% - 40px)
|
|
.view-title
|
|
color #eee
|
|
font-size 20px
|
|
text-align center
|
|
margin 10px auto
|
|
position relative
|
|
i.el-icon-goods
|
|
margin-left 4px
|
|
font-size 20px
|
|
vertical-align middle
|
|
cursor pointer
|
|
transition color .2s ease-in-out
|
|
&:hover
|
|
color #49B1F5
|
|
i.el-icon-update
|
|
position absolute
|
|
right 35px
|
|
top 8px
|
|
font-size 20px
|
|
vertical-align middle
|
|
cursor pointer
|
|
transition color .2s ease-in-out
|
|
&:hover
|
|
color #49B1F5
|
|
i.el-icon-download
|
|
position absolute
|
|
right 5px
|
|
top 8px
|
|
font-size 20px
|
|
vertical-align middle
|
|
cursor pointer
|
|
transition color .2s ease-in-out
|
|
&:hover
|
|
color #49B1F5
|
|
.handle-bar
|
|
margin-bottom 20px
|
|
&.cut-width
|
|
padding-right: 8px
|
|
.el-input__inner
|
|
border-radius 0
|
|
.plugin-item
|
|
box-sizing border-box
|
|
height 80px
|
|
background #444
|
|
padding 8px
|
|
user-select text
|
|
transition all .2s ease-in-out
|
|
position relative
|
|
&__container
|
|
height 80px
|
|
margin-bottom 10px
|
|
.cli-only-badge
|
|
position absolute
|
|
right 0px
|
|
top 0
|
|
font-size 12px
|
|
padding 3px 8px
|
|
background #49B1F5
|
|
color #eee
|
|
&.darwin
|
|
background transparentify($darwinBg, #000, 0.75)
|
|
&:hover
|
|
background transparentify($darwinBg, #000, 0.85)
|
|
&:hover
|
|
background #333
|
|
&__logo
|
|
width 64px
|
|
height 64px
|
|
float left
|
|
&__content
|
|
float left
|
|
width calc(100% - 72px)
|
|
height 64px
|
|
color #ddd
|
|
margin-left 8px
|
|
display flex
|
|
flex-direction column
|
|
justify-content space-between
|
|
&.disabled
|
|
color #aaa
|
|
&__name
|
|
font-size 16px
|
|
height 22px
|
|
line-height 22px
|
|
font-weight 600
|
|
cursor pointer
|
|
text-overflow ellipsis
|
|
white-space nowrap
|
|
overflow hidden
|
|
transition all .2s ease-in-out
|
|
&:hover
|
|
color: #1B9EF3
|
|
&__desc
|
|
font-size 14px
|
|
height 21px
|
|
line-height 21px
|
|
overflow hidden
|
|
text-overflow ellipsis
|
|
white-space nowrap
|
|
&__info-bar
|
|
font-size 14px
|
|
height 21px
|
|
line-height 28px
|
|
position relative
|
|
&__author
|
|
overflow hidden
|
|
text-overflow ellipsis
|
|
white-space nowrap
|
|
&__config
|
|
float right
|
|
font-size 16px
|
|
cursor pointer
|
|
transition all .2s ease-in-out
|
|
&:hover
|
|
color: #1B9EF3
|
|
.config-button
|
|
font-size 12px
|
|
color #ddd
|
|
background #222
|
|
padding 1px 8px
|
|
height 18px
|
|
line-height 18px
|
|
text-align center
|
|
position absolute
|
|
top 4px
|
|
right 20px
|
|
transition all .2s ease-in-out
|
|
&.reload
|
|
right 0px
|
|
&.ing
|
|
right 0px
|
|
&.install
|
|
right 0px
|
|
&:hover
|
|
background: #1B9EF3
|
|
color #fff
|
|
.reload-mask
|
|
position absolute
|
|
width calc(100% - 40px)
|
|
bottom -320px
|
|
text-align center
|
|
background rgba(0,0,0,0.4)
|
|
padding 10px 0
|
|
&.cut-width
|
|
width calc(100% - 48px)
|
|
</style>
|