admin-theme.vue 8.2 KB

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