2-2 LText 组件初步实现

  1. <template>
  2. <div class="editor-container">
  3. <a-layout>
  4. <a-layout-sider width="300" style="background: #fff">
  5. <div class="sidebar-container">组件列表</div>
  6. </a-layout-sider>
  7. <a-layout style="padding: 0 24px 24px">
  8. <a-layout-content class="preview-container">
  9. <p>画布区域</p>
  10. <!--引用的是component-->
  11. <component
  12. class="preview-list"
  13. id="canvas-area"
  14. v-for="item in components"
  15. :key="item.id"
  16. :is="item.name"
  17. v-bind="item.props"
  18. >
  19. </component>
  20. </a-layout-content>
  21. </a-layout>
  22. <a-layout-sider
  23. width="300"
  24. style="background: #fff"
  25. class="settings-panel"
  26. >
  27. 组件属性
  28. </a-layout-sider>
  29. </a-layout>
  30. </div>
  31. </template>
  32. <script lang="ts">
  33. import { GloabalDataProps } from '@/store'
  34. import { defineComponent, computed } from 'vue'
  35. import { useStore } from 'vuex'
  36. import LText from '@/components/LText.vue'
  37. export default defineComponent({
  38. components: { LText },
  39. setup() {
  40. const store = useStore<GloabalDataProps>()
  41. return {
  42. components: store.state.editor.components,
  43. }
  44. },
  45. })
  46. </script>
  47. <style lang="less" scoped>
  48. .editor-container .preview-container {
  49. padding: 24px;
  50. margin: 0;
  51. display: flex;
  52. flex-direction: column;
  53. align-items: center;
  54. position: relative;
  55. }
  56. .editor-container .preview-list {
  57. padding: 0;
  58. margin: 0;
  59. border: 1px solid #efefef;
  60. background: #fff;
  61. overflow-x: hidden;
  62. overflow-y: auto;
  63. position: fixed;
  64. }
  65. </style>
  1. <template>
  2. <!--component 的tag是一个的动态组件-->
  3. <component :is="tag" :style="styleProps" class="l-text-component">
  4. {{ text }}
  5. </component>
  6. </template>
  7. <script>
  8. import { computed, defineComponent, PropType } from 'vue'
  9. import _ from 'lodash-es'
  10. export default defineComponent({
  11. name: 'l-text',
  12. props: {
  13. text: {
  14. type: String,
  15. },
  16. fontSize: {
  17. type: String,
  18. },
  19. tag: {
  20. type: String,
  21. default: 'div',
  22. },
  23. },
  24. setup(props) {
  25. console.log(props)
  26. const styleProps = computed(() => _.pick(props, ['fontSize']))
  27. return {
  28. styleProps,
  29. }
  30. },
  31. })
  32. </script>
  33. <style lang="less" scoped>
  34. h2.l-text-component,
  35. p.l-text-component {
  36. margin-bottom: 0;
  37. }
  38. button.l-text-component {
  39. padding: 5px 10px;
  40. cursor: pointer;
  41. }
  42. .l-text-component {
  43. box-sizing: border-box;
  44. white-space: pre-wrap;
  45. position: relative !important;
  46. width: 100%;
  47. height: 100%;
  48. }
  49. </style>

2-3 LText 添加通用属性

配置组件的通用默认属性以及 l-text 组件的特有默认属性。

  1. ...
  2. <script>
  3. import { computed, defineComponent, PropType } from 'vue'
  4. import _ from 'lodash-es'
  5. import {
  6. transformToComponentProps,
  7. textDefaultProps,
  8. textStylePropNames,
  9. } from '@/ts/defaultProps'
  10. const defaultProps = transformToComponentProps(textDefaultProps)
  11. //增加其他通用属性
  12. export default defineComponent({
  13. name: 'l-text',
  14. props: {
  15. tag: {
  16. type: String,
  17. default: 'div',
  18. },
  19. ...defaultProps,
  20. },
  21. setup(props) {
  22. const styleProps = computed(() => _.pick(props, textStylePropNames))
  23. return {
  24. styleProps,
  25. }
  26. },
  27. })
  28. </script>
  29. ...
  1. import _, { without } from 'lodash-es'
  2. export interface CommonComponentProps {
  3. // actions
  4. actionType: string
  5. url: string
  6. // size
  7. height: string
  8. width: string
  9. paddingLeft: string
  10. paddingRight: string
  11. paddingTop: string
  12. paddingBottom: string
  13. // border type
  14. borderStyle: string
  15. borderColor: string
  16. borderWidth: string
  17. borderRadius: string
  18. // shadow and opacity
  19. boxShadow: string
  20. opacity: string
  21. // position and x,y
  22. position: string
  23. left: string
  24. top: string
  25. right: string
  26. }
  27. export const commonDefaultProps: CommonComponentProps = {
  28. // actions
  29. actionType: '',
  30. url: '',
  31. // size
  32. height: '',
  33. width: '373px',
  34. paddingLeft: '0px',
  35. paddingRight: '0px',
  36. paddingTop: '0px',
  37. paddingBottom: '0px',
  38. // border type
  39. borderStyle: 'none',
  40. borderColor: '#000',
  41. borderWidth: '0',
  42. borderRadius: '0',
  43. // shadow and opacity
  44. boxShadow: '0 0 0 #000000',
  45. opacity: '1',
  46. // position and x,y
  47. position: 'absolute',
  48. left: '0',
  49. top: '0',
  50. right: '0',
  51. }
  52. export interface TextComponentProps extends CommonComponentProps {
  53. text: string
  54. fontSize: string
  55. fontFamily: string
  56. fontWeight: string
  57. fontStyle: string
  58. textDecoration: string
  59. lineHeight: string
  60. textAlign: string
  61. color: string
  62. backgroundColor: string
  63. }
  64. //extends 追加
  65. export interface TextComponentProps extends CommonComponentProps {
  66. text: string
  67. fontSize: string
  68. fontFamily: string
  69. fontWeight: string
  70. fontStyle: string
  71. textDecoration: string
  72. lineHeight: string
  73. textAlign: string
  74. color: string
  75. backgroundColor: string
  76. }
  77. export const textDefaultProps: TextComponentProps = {
  78. // basic props - font styles
  79. text: '正文内容',
  80. fontSize: '14px',
  81. fontFamily: '',
  82. fontWeight: 'normal',
  83. fontStyle: 'normal',
  84. textDecoration: 'none',
  85. lineHeight: '1',
  86. textAlign: 'left',
  87. color: '#000000',
  88. backgroundColor: '',
  89. ...commonDefaultProps,
  90. }
  91. export interface ImageComponentProps extends CommonComponentProps {
  92. src: string
  93. }
  94. export const imageDefaultProps: ImageComponentProps = {
  95. src: 'test.url',
  96. ...commonDefaultProps,
  97. }
  98. // 排除非样式属性
  99. export const textStylePropNames = without(
  100. Object.keys(textDefaultProps),
  101. 'actionType',
  102. 'url',
  103. 'text',
  104. )
  105. // 转换成组件的props属性
  106. export const transformToComponentProps = (props: { [key: string]: any }) => {
  107. return _.mapValues(props, (item) => {
  108. return {
  109. type: item.constructor,
  110. default: item,
  111. }
  112. })
  113. }

2-4 LText 使用 hooks 重用逻辑

封装一个 hooks ,挑选出样式属性,并返回一个点击事件处理函数。

  1. import { computed } from 'vue'
  2. import _ from 'lodash-es'
  3. export default function useComponentCommon<T extends { [key: string]: any }>(
  4. props: T,
  5. picks: string[],
  6. ) {
  7. const styleProps = computed(() => _.pick(props, picks))
  8. const handleClick = () => {
  9. if (props.actionType === 'url' && props.url) {
  10. window.location.href = props.url
  11. }
  12. }
  13. //事件类型:无|跳转URL下拉菜单
  14. return { styleProps, handleClick }
  15. }
  1. <template>
  2. <!--component 的tag是一个的动态组件-->
  3. <component
  4. :is="tag"
  5. :style="styleProps"
  6. class="l-text-component"
  7. @click="handleClick"
  8. >
  9. {{ text }}
  10. </component>
  11. </template>
  12. <script>
  13. import { computed, defineComponent, PropType } from 'vue'
  14. import _ from 'lodash-es'
  15. import {
  16. transformToComponentProps,
  17. textDefaultProps,
  18. textStylePropNames,
  19. } from '@/ts/defaultProps'
  20. import useComponentCommon from '@/hooks/useComponentCommon'
  21. const defaultProps = transformToComponentProps(textDefaultProps)
  22. export default defineComponent({
  23. name: 'l-text',
  24. props: {
  25. tag: {
  26. type: String,
  27. default: 'div',
  28. },
  29. ...defaultProps,
  30. },
  31. setup(props) {
  32. console.log(props)
  33. const { styleProps, handleClick } = useComponentCommon(
  34. props,
  35. textStylePropNames,
  36. )
  37. return {
  38. styleProps,
  39. handleClick,
  40. }
  41. },
  42. })
  43. </script>
  44. ...

image.png