应用商店
我对这个文件做了一个小小的重构,首先添加结构。这里我修改了存储结构,添加了一个 showname
,这样比显示 filename
更好看一些。
<Back/>
<div class="wrap">
<h2>插件列表</h2>
<button class="d-btn" on:click="refresh()">刷新</button>
<ul ref:list>
{#each plugins as plugin,index}
<li in:fade class="plugin">
<a class="title" target="_blank" href={plugin.repository} title="点我跳转到仓库页">{plugin.showname}</a>
<p class="info {infoIndex == index? '': 'hidden'}" on:click="showInfo(index)">
{@html marked(plugin.description || '')}
</p>
<p class="control">
{plugin.author}
<span class="right">
{#if plugin.downloaded }
<button class="d-btn del" on:click="del(plugin.filename)">
{#if inWorking(workingFilename, plugin.filename)}
···
{:else}
删除
{/if}
</button>
{:else}
<button class="d-btn green" on:click="download(plugin.filename, plugin.url)">
{#if inWorking(workingFilename, plugin.filename)}
···
{:else}
下载
{/if}
</button>
{/if}
</span>
</p>
</li>
{/each}
</ul>
</div>
导入依赖与样式
svelte 包含一些额外的组件,从里面我们导入一些动画和辅助方法(数组推入,删除、补间动画等等)。
import { ipcRenderer as ipc, shell } from 'electron'
import marked from 'marked'
import { fade } from 'svelte-transitions'
import { push, splice } from 'svelte-extras'
添加 helper
function addDownloaed(plugins, local_plugins) {
// 判断下载过的插件
return plugins.map(plugin => {
plugin.downloaded =
local_plugins.findIndex(
local_plugin => local_plugin == plugin.filename
) >= 0
return plugin
})
}
function inWorking(workingFilename, filename) {
return workingFilename.indexOf(filename) >= 0
}
配置项
infoIndex
用来控制详情的展开项,workingFilename
代表着正在进行操作的文件,对于发送过一次,我们必须进行一下锁定,要不然用户点击太多次容易忙不过来。
destroy
的时候一定要移除监听,要不然会造成内存泄露。
export default {
data() {
return {
local_plugins: [], // 本地插件
all_plugins: [], // 所有插件
infoIndex: -1, // 展开详情
workingFilename: [] // 操作中文件
}
},
components: {
Back: '../components/Back.svelte'
},
helpers: {
marked, // markdown 处理
inWorking // 是否处理中
},
transitions: {
fade
},
computed: {
plugins: ({ all_plugins, local_plugins }) =>
addDownloaed(all_plugins, local_plugins)
},
methods: {
push,
splice,
showInfo(index) {
// 显示某一个插件介绍
const oldIndex = this.get().infoIndex
if (oldIndex == index) {
return this.set({
infoIndex: -1
})
}
this.set({
infoIndex: index
})
},
refresh() {
// 刷新
ipc.send('reload_local_plugins')
ipc.send('reload_all_plugins')
},
download(filename, url) {
// 下载插件
if (inWorking(this.get().workingFilename, filename)) {
return
}
this.push('workingFilename', filename)
this.store.setMsg('info', `下载插件中`)
ipc.send('download_plugin', {
filename,
url
})
},
del(filename) {
// 删除插件
if (inWorking(this.get().workingFilename, filename)) {
return
}
this.push('workingFilename', filename)
this.store.setMsg('info', `删除插件中`)
ipc.send('delete_plugin', {
filename
})
},
all_plugins(event, all_plugins) {
this.set({
all_plugins
})
},
local_plugins(event, local_plugins) {
this.set({
local_plugins
})
},
download_plugin_success(event, filename) {
// 下载成功
const filenames = this.get().workingFilename || []
let index = filenames.indexOf(filename)
if (index >= 0) {
this.splice('workingFilename', index, 1)
}
if (this.get().workingFilename == 0) this.store.success('下载完成')
},
delete_plugin_success(event, filename) {
// 删除成功
const filenames = this.get().workingFilename || []
let index = filenames.indexOf(filename)
if (index >= 0) {
this.splice('workingFilename', index, 1)
}
if (this.get().workingFilename == 0) this.store.success('删除完成')
}
},
ondestroy() {
// 清理监听
ipc.removeListener('all_plugins', this.all_plugins.bind(this))
ipc.removeListener('local_plugins', this.local_plugins.bind(this))
ipc.removeListener(
'download_plugin_success',
this.download_plugin_success.bind(this)
)
ipc.removeListener(
'delete_plugin_success',
this.delete_plugin_success.bind(this)
)
},
oncreate() {
// 添加监听
ipc.send('get_all_plugins')
ipc.send('get_local_plugins')
ipc.on('all_plugins', this.all_plugins.bind(this))
ipc.on('local_plugins', this.local_plugins.bind(this))
ipc.on('download_plugin_success', this.download_plugin_success.bind(this))
ipc.on('delete_plugin_success', this.delete_plugin_success.bind(this))
this.refs.list.addEventListener(
// 处理点击事件,外部浏览器打开
'click',
e => {
if (e.target.tagName.toLowerCase() == 'a') {
e.preventDefault()
shell.openExternal(e.target.href)
}
},
true
)
}
}
样式
<style>
h2 {
color: #fff;
font-weight: 600;
padding: 1rem .5rem;
}
.wrap {
height: 100vh;
background: #303238;
}
h2~button {
margin-left: .5rem;
margin-bottom: 1rem;
}
ul {
list-style: none;
margin: .5rem
}
.plugin {
padding: .5rem;
background: #383A41;
border-radius: .2rem;
margin: .5rem 0;
}
.title {
color: #fff;
text-decoration: none;
font-size: 20px;
outline: none;
font-weight: 900;
}
.info {
color: #fff;
font-size: .89rem;
margin: .3rem 0;
cursor: default;
font-weight: 300;
transition: all .2s;
max-width: 100%;
}
.info :global(a) {
text-decoration: none;
color: #fff;
}
.info :global(a):hover {
background: #fff;
color: black;
}
.info.hidden {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.control {
font-size: .8rem;
color: #fff;
display: flex;
align-items: center;
}
.right {
margin-left: auto;
}
.d-btn {
padding: .3rem 1rem;
color: #fff;
background: #54B3FF;
border: none;
outline: none;
cursor: pointer;
border-radius: 2px;
}
.del {
background: transparent;
}
.green {
background: #4BCB7C;
}
.solid {
border: 1px solid #fff
}
</style>
添加主界面
新建 pages/Main.svelte
页面,别忘记在 store 里面添加路由映射, 并且默认显示该页面。
<div class="wrap">
<Link className="download-page-btn" to="Download">下载页面</Link>
<Link className="music-page-btn" to="Music">播放器</Link>
<Link className="store-page-btn" to="CrawlStore">爬虫商店</Link>
</div>
<script>
export default {
components: {
Link: '../components/Link.svelte'
},
};
</script>
<style>
.wrap {
display: flex;
height: 100vh;
flex-wrap: wrap;
background: #333;
}
.wrap :global(a) {
display: inline-flex;
width: 50%;
text-decoration: none;
justify-content: center;
align-items: center;
color: #fff;
font-size: 1rem;
letter-spacing: 5px;
transition: all .2s;
}
.wrap :global(a):hover {
background: #fb584c
}
</style>
修改窗口创建参数
创建一个无边框的应用。
mainWindow = createMainWindow({
width: 400,
height: 560,
frame: false,
transparent: true
})