admin-theme.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row wrap)
  4. v-flex(xs12)
  5. .admin-header
  6. img.animated.fadeInUp(src='/svg/icon-paint-palette.svg', alt='Theme', style='width: 80px;')
  7. .admin-header-title
  8. .headline.primary--text.animated.fadeInLeft {{$t('admin:theme.title')}}
  9. .subheading.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:theme.subtitle')}}
  10. v-spacer
  11. v-btn.animated.fadeInRight(color='success', depressed, @click='save', large, :loading='loading')
  12. v-icon(left) mdi-check
  13. span {{$t('common:actions.apply')}}
  14. v-form.pt-3
  15. v-layout(row wrap)
  16. v-flex(lg6 xs12)
  17. v-card.wiki-form.animated.fadeInUp
  18. v-toolbar(color='primary', dark, dense, flat)
  19. v-toolbar-title.subtitle-1 {{$t('admin:theme.title')}}
  20. v-card-text
  21. v-select(
  22. :items='themes'
  23. outlined
  24. prepend-icon='mdi-palette'
  25. v-model='config.theme'
  26. :label='$t(`admin:theme.siteTheme`)'
  27. persistent-hint
  28. :hint='$t(`admin:theme.siteThemeHint`)'
  29. )
  30. template(slot='item', slot-scope='data')
  31. v-list-item-avatar
  32. v-icon.blue--text(dark) filter_frames
  33. v-list-item-content
  34. v-list-item-title(v-html='data.item.text')
  35. v-list-item-sub-title(v-html='data.item.author')
  36. v-select.mt-3(
  37. :items='iconsets'
  38. outlined
  39. prepend-icon='mdi-paw'
  40. v-model='config.iconset'
  41. :label='$t(`admin:theme.iconset`)'
  42. persistent-hint
  43. :hint='$t(`admin:theme.iconsetHint`)'
  44. )
  45. v-divider.mt-3
  46. v-switch(
  47. v-model='darkMode'
  48. :label='$t(`admin:theme.darkMode`)'
  49. color='primary'
  50. persistent-hint
  51. :hint='$t(`admin:theme.darkModeHint`)'
  52. )
  53. v-card.wiki-form.mt-3.animated.fadeInUp.wait-p2s
  54. v-toolbar(color='primary', dark, dense, flat)
  55. v-toolbar-title.subtitle-1 {{$t(`admin:theme.codeInjection`)}}
  56. v-card-text
  57. v-textarea(
  58. v-model='config.injectCSS'
  59. :label='$t(`admin:theme.cssOverride`)'
  60. outlined
  61. color='primary'
  62. persistent-hint
  63. :hint='$t(`admin:theme.cssOverrideHint`)'
  64. auto-grow
  65. )
  66. i18next.caption.pl-2.ml-1(path='admin:theme.cssOverrideWarning', tag='div')
  67. strong.red--text(place='caution') {{$t('admin:theme.cssOverrideWarningCaution')}}
  68. code(place='cssClass') .contents
  69. v-textarea.mt-3(
  70. v-model='config.injectHead'
  71. :label='$t(`admin:theme.headHtmlInjection`)'
  72. outlined
  73. color='primary'
  74. persistent-hint
  75. :hint='$t(`admin:theme.headHtmlInjectionHint`)'
  76. auto-grow
  77. )
  78. v-textarea.mt-2(
  79. v-model='config.injectBody'
  80. :label='$t(`admin:theme.bodyHtmlInjection`)'
  81. outlined
  82. color='primary'
  83. persistent-hint
  84. :hint='$t(`admin:theme.bodyHtmlInjectionHint`)'
  85. auto-grow
  86. )
  87. v-flex(lg6 xs12)
  88. v-card.animated.fadeInUp.wait-p2s
  89. v-toolbar(color='teal', dark, dense, flat)
  90. v-toolbar-title.subtitle-1 {{$t('admin:theme.downloadThemes')}}
  91. v-spacer
  92. v-chip(label, color='white', small).teal--text coming soon
  93. v-data-table(
  94. :headers='headers',
  95. :items='themes',
  96. hide-default-footer,
  97. item-key='value',
  98. :items-per-page='1000'
  99. )
  100. template(v-slot:items='thm')
  101. td
  102. strong {{thm.item.text}}
  103. td
  104. span {{ thm.item.author }}
  105. td.text-xs-center
  106. v-progress-circular(v-if='thm.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
  107. v-btn(v-else-if='thm.item.isInstalled && thm.item.installDate < thm.item.updatedAt', icon)
  108. v-icon.blue--text mdi-cached
  109. v-btn(v-else-if='thm.item.isInstalled', icon)
  110. v-icon.green--text mdi-check
  111. v-btn(v-else, icon)
  112. v-icon.grey--text mdi-cloud-download
  113. </template>
  114. <script>
  115. import _ from 'lodash'
  116. import { sync } from 'vuex-pathify'
  117. import themeConfigQuery from 'gql/admin/theme/theme-query-config.gql'
  118. import themeSaveMutation from 'gql/admin/theme/theme-mutation-save.gql'
  119. export default {
  120. data() {
  121. return {
  122. loading: false,
  123. themes: [
  124. { text: 'Default', author: 'requarks.io', value: 'default', isInstalled: true, installDate: '', updatedAt: '' }
  125. ],
  126. iconsets: [
  127. { text: 'Material Design Icons (default)', value: 'mdi' },
  128. { text: 'Font Awesome 5', value: 'fa' },
  129. { text: 'Font Awesome 4', value: 'fa4' }
  130. ],
  131. config: {
  132. theme: 'default',
  133. darkMode: false,
  134. iconset: '',
  135. injectCSS: '',
  136. injectHead: '',
  137. injectBody: ''
  138. },
  139. darkModeInitial: false
  140. }
  141. },
  142. computed: {
  143. darkMode: sync('site/dark'),
  144. headers() {
  145. return [
  146. {
  147. text: this.$t('admin:theme.downloadName'),
  148. align: 'left',
  149. value: 'text'
  150. },
  151. {
  152. text: this.$t('admin:theme.downloadAuthor'),
  153. align: 'left',
  154. value: 'author'
  155. },
  156. {
  157. text: this.$t('admin:theme.downloadDownload'),
  158. align: 'center',
  159. value: 'value',
  160. sortable: false,
  161. width: 100
  162. }
  163. ]
  164. }
  165. },
  166. mounted() {
  167. this.darkModeInitial = this.darkMode
  168. },
  169. beforeDestroy() {
  170. this.darkMode = this.darkModeInitial
  171. },
  172. methods: {
  173. async save () {
  174. this.loading = true
  175. this.$store.commit(`loadingStart`, 'admin-theme-save')
  176. try {
  177. const respRaw = await this.$apollo.mutate({
  178. mutation: themeSaveMutation,
  179. variables: {
  180. theme: this.config.theme,
  181. iconset: this.config.iconset,
  182. darkMode: this.darkMode,
  183. injectCSS: this.config.injectCSS,
  184. injectHead: this.config.injectHead,
  185. injectBody: this.config.injectBody
  186. }
  187. })
  188. const resp = _.get(respRaw, 'data.theming.setConfig.responseResult', {})
  189. if (resp.succeeded) {
  190. this.darkModeInitial = this.darkMode
  191. this.$store.commit('showNotification', {
  192. message: 'Theme settings updated successfully.',
  193. style: 'success',
  194. icon: 'check'
  195. })
  196. } else {
  197. throw new Error(resp.message)
  198. }
  199. } catch (err) {
  200. this.$store.commit('pushGraphError', err)
  201. }
  202. this.$store.commit(`loadingStop`, 'admin-theme-save')
  203. this.loading = false
  204. }
  205. },
  206. apollo: {
  207. config: {
  208. query: themeConfigQuery,
  209. fetchPolicy: 'network-only',
  210. update: (data) => data.theming.config,
  211. watchLoading (isLoading) {
  212. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-theme-refresh')
  213. }
  214. }
  215. }
  216. }
  217. </script>
  218. <style lang='scss'>
  219. </style>