应用商店

我对这个文件做了一个小小的重构,首先添加结构。这里我修改了存储结构,添加了一个 showname,这样比显示 filename 更好看一些。

  1. <Back/>
  2. <div class="wrap">
  3. <h2>插件列表</h2>
  4. <button class="d-btn" on:click="refresh()">刷新</button>
  5. <ul ref:list>
  6. {#each plugins as plugin,index}
  7. <li in:fade class="plugin">
  8. <a class="title" target="_blank" href={plugin.repository} title="点我跳转到仓库页">{plugin.showname}</a>
  9. <p class="info {infoIndex == index? '': 'hidden'}" on:click="showInfo(index)">
  10. {@html marked(plugin.description || '')}
  11. </p>
  12. <p class="control">
  13. {plugin.author}
  14. <span class="right">
  15. {#if plugin.downloaded }
  16. <button class="d-btn del" on:click="del(plugin.filename)">
  17. {#if inWorking(workingFilename, plugin.filename)}
  18. ···
  19. {:else}
  20. 删除
  21. {/if}
  22. </button>
  23. {:else}
  24. <button class="d-btn green" on:click="download(plugin.filename, plugin.url)">
  25. {#if inWorking(workingFilename, plugin.filename)}
  26. ···
  27. {:else}
  28. 下载
  29. {/if}
  30. </button>
  31. {/if}
  32. </span>
  33. </p>
  34. </li>
  35. {/each}
  36. </ul>
  37. </div>

导入依赖与样式

svelte 包含一些额外的组件,从里面我们导入一些动画和辅助方法(数组推入,删除、补间动画等等)。

  1. import { ipcRenderer as ipc, shell } from 'electron'
  2. import marked from 'marked'
  3. import { fade } from 'svelte-transitions'
  4. import { push, splice } from 'svelte-extras'

添加 helper

  1. function addDownloaed(plugins, local_plugins) {
  2. // 判断下载过的插件
  3. return plugins.map(plugin => {
  4. plugin.downloaded =
  5. local_plugins.findIndex(
  6. local_plugin => local_plugin == plugin.filename
  7. ) >= 0
  8. return plugin
  9. })
  10. }
  11. function inWorking(workingFilename, filename) {
  12. return workingFilename.indexOf(filename) >= 0
  13. }

 配置项

infoIndex 用来控制详情的展开项,workingFilename 代表着正在进行操作的文件,对于发送过一次,我们必须进行一下锁定,要不然用户点击太多次容易忙不过来。

destroy 的时候一定要移除监听,要不然会造成内存泄露。

  1. export default {
  2. data() {
  3. return {
  4. local_plugins: [], // 本地插件
  5. all_plugins: [], // 所有插件
  6. infoIndex: -1, // 展开详情
  7. workingFilename: [] // 操作中文件
  8. }
  9. },
  10. components: {
  11. Back: '../components/Back.svelte'
  12. },
  13. helpers: {
  14. marked, // markdown 处理
  15. inWorking // 是否处理中
  16. },
  17. transitions: {
  18. fade
  19. },
  20. computed: {
  21. plugins: ({ all_plugins, local_plugins }) =>
  22. addDownloaed(all_plugins, local_plugins)
  23. },
  24. methods: {
  25. push,
  26. splice,
  27. showInfo(index) {
  28. // 显示某一个插件介绍
  29. const oldIndex = this.get().infoIndex
  30. if (oldIndex == index) {
  31. return this.set({
  32. infoIndex: -1
  33. })
  34. }
  35. this.set({
  36. infoIndex: index
  37. })
  38. },
  39. refresh() {
  40. // 刷新
  41. ipc.send('reload_local_plugins')
  42. ipc.send('reload_all_plugins')
  43. },
  44. download(filename, url) {
  45. // 下载插件
  46. if (inWorking(this.get().workingFilename, filename)) {
  47. return
  48. }
  49. this.push('workingFilename', filename)
  50. this.store.setMsg('info', `下载插件中`)
  51. ipc.send('download_plugin', {
  52. filename,
  53. url
  54. })
  55. },
  56. del(filename) {
  57. // 删除插件
  58. if (inWorking(this.get().workingFilename, filename)) {
  59. return
  60. }
  61. this.push('workingFilename', filename)
  62. this.store.setMsg('info', `删除插件中`)
  63. ipc.send('delete_plugin', {
  64. filename
  65. })
  66. },
  67. all_plugins(event, all_plugins) {
  68. this.set({
  69. all_plugins
  70. })
  71. },
  72. local_plugins(event, local_plugins) {
  73. this.set({
  74. local_plugins
  75. })
  76. },
  77. download_plugin_success(event, filename) {
  78. // 下载成功
  79. const filenames = this.get().workingFilename || []
  80. let index = filenames.indexOf(filename)
  81. if (index >= 0) {
  82. this.splice('workingFilename', index, 1)
  83. }
  84. if (this.get().workingFilename == 0) this.store.success('下载完成')
  85. },
  86. delete_plugin_success(event, filename) {
  87. // 删除成功
  88. const filenames = this.get().workingFilename || []
  89. let index = filenames.indexOf(filename)
  90. if (index >= 0) {
  91. this.splice('workingFilename', index, 1)
  92. }
  93. if (this.get().workingFilename == 0) this.store.success('删除完成')
  94. }
  95. },
  96. ondestroy() {
  97. // 清理监听
  98. ipc.removeListener('all_plugins', this.all_plugins.bind(this))
  99. ipc.removeListener('local_plugins', this.local_plugins.bind(this))
  100. ipc.removeListener(
  101. 'download_plugin_success',
  102. this.download_plugin_success.bind(this)
  103. )
  104. ipc.removeListener(
  105. 'delete_plugin_success',
  106. this.delete_plugin_success.bind(this)
  107. )
  108. },
  109. oncreate() {
  110. // 添加监听
  111. ipc.send('get_all_plugins')
  112. ipc.send('get_local_plugins')
  113. ipc.on('all_plugins', this.all_plugins.bind(this))
  114. ipc.on('local_plugins', this.local_plugins.bind(this))
  115. ipc.on('download_plugin_success', this.download_plugin_success.bind(this))
  116. ipc.on('delete_plugin_success', this.delete_plugin_success.bind(this))
  117. this.refs.list.addEventListener(
  118. // 处理点击事件,外部浏览器打开
  119. 'click',
  120. e => {
  121. if (e.target.tagName.toLowerCase() == 'a') {
  122. e.preventDefault()
  123. shell.openExternal(e.target.href)
  124. }
  125. },
  126. true
  127. )
  128. }
  129. }

样式

  1. <style>
  2. h2 {
  3. color: #fff;
  4. font-weight: 600;
  5. padding: 1rem .5rem;
  6. }
  7. .wrap {
  8. height: 100vh;
  9. background: #303238;
  10. }
  11. h2~button {
  12. margin-left: .5rem;
  13. margin-bottom: 1rem;
  14. }
  15. ul {
  16. list-style: none;
  17. margin: .5rem
  18. }
  19. .plugin {
  20. padding: .5rem;
  21. background: #383A41;
  22. border-radius: .2rem;
  23. margin: .5rem 0;
  24. }
  25. .title {
  26. color: #fff;
  27. text-decoration: none;
  28. font-size: 20px;
  29. outline: none;
  30. font-weight: 900;
  31. }
  32. .info {
  33. color: #fff;
  34. font-size: .89rem;
  35. margin: .3rem 0;
  36. cursor: default;
  37. font-weight: 300;
  38. transition: all .2s;
  39. max-width: 100%;
  40. }
  41. .info :global(a) {
  42. text-decoration: none;
  43. color: #fff;
  44. }
  45. .info :global(a):hover {
  46. background: #fff;
  47. color: black;
  48. }
  49. .info.hidden {
  50. white-space: nowrap;
  51. text-overflow: ellipsis;
  52. overflow: hidden;
  53. }
  54. .control {
  55. font-size: .8rem;
  56. color: #fff;
  57. display: flex;
  58. align-items: center;
  59. }
  60. .right {
  61. margin-left: auto;
  62. }
  63. .d-btn {
  64. padding: .3rem 1rem;
  65. color: #fff;
  66. background: #54B3FF;
  67. border: none;
  68. outline: none;
  69. cursor: pointer;
  70. border-radius: 2px;
  71. }
  72. .del {
  73. background: transparent;
  74. }
  75. .green {
  76. background: #4BCB7C;
  77. }
  78. .solid {
  79. border: 1px solid #fff
  80. }
  81. </style>

添加主界面

新建 pages/Main.svelte 页面,别忘记在 store 里面添加路由映射, 并且默认显示该页面。

  1. <div class="wrap">
  2. <Link className="download-page-btn" to="Download">下载页面</Link>
  3. <Link className="music-page-btn" to="Music">播放器</Link>
  4. <Link className="store-page-btn" to="CrawlStore">爬虫商店</Link>
  5. </div>
  6. <script>
  7. export default {
  8. components: {
  9. Link: '../components/Link.svelte'
  10. },
  11. };
  12. </script>
  13. <style>
  14. .wrap {
  15. display: flex;
  16. height: 100vh;
  17. flex-wrap: wrap;
  18. background: #333;
  19. }
  20. .wrap :global(a) {
  21. display: inline-flex;
  22. width: 50%;
  23. text-decoration: none;
  24. justify-content: center;
  25. align-items: center;
  26. color: #fff;
  27. font-size: 1rem;
  28. letter-spacing: 5px;
  29. transition: all .2s;
  30. }
  31. .wrap :global(a):hover {
  32. background: #fb584c
  33. }
  34. </style>

修改窗口创建参数

创建一个无边框的应用。

  1. mainWindow = createMainWindow({
  2. width: 400,
  3. height: 560,
  4. frame: false,
  5. transparent: true
  6. })