Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于文件(图片)类型的jsonschema编写体验、表单面板的UI及交互体验的一点提议。 #52

Closed
jobyrao opened this issue Mar 22, 2021 · 5 comments

Comments

@jobyrao
Copy link

jobyrao commented Mar 22, 2021

文件是比较特殊的类型,其中图片甚之。

目前图片类型json schema书写如下:

"imgUrl": {
    "title": "单个图片",
    "type": "string",
    "default": "http://img.alicdn.com/tfs/TB1vYlkdnZmx1VjSZFGXXax2XXa-468-644.jpg_320x5000q100.jpg_.webp",
    "ui:action": "https://run.mocky.io/v3/518d7af7-204f-45ab-9628-a6e121dab8ca",
    "ui:widget": "UploadWidget",
    "ui:btnText": "上传按钮文案配置"
}

表单面板展示如下:
image

提议1

  1. json schema编写支持图片的image类型。
  2. 表单面板支持input输入。
"imgUrl": {
    "title": "单个图片",
    "type": "image",
    "default": "http://img.alicdn.com/tfs/TB1vYlkdnZmx1VjSZFGXXax2XXa-468-644.jpg_320x5000q100.jpg_.webp",
    "ui:action": "https://run.mocky.io/v3/518d7af7-204f-45ab-9628-a6e121dab8ca"
}

image

提议2

以作者的活动编辑器来讲,文件的上传是否可以交到这一层(编辑器开发者、动态表单方案采纳者?)统一处理,而无需组件开发者重复填写ui:action地址。

<vue-form
    v-model="formData"
    :schema="schema"
    :beforeUpload="uploadCover"
/>
"imgUrl": {
    "title": "单个图片",
    "type": "image",
    "default": "http://img.alicdn.com/tfs/TB1vYlkdnZmx1VjSZFGXXax2XXa-468-644.jpg_320x5000q100.jpg_.webp"
}

这样的支持,对于可视化(组件化)中台来讲,组件开发者会轻松许多。

😃 盼回复~

@lljj-x
Copy link
Owner

lljj-x commented Mar 22, 2021

其实原本的设计,是希望如果需要符合业务的场景可以去定义自己的 Widget 比如这里的上传


提议1

  • json schema编写支持图片的image类型,并且支持支持input输入。

这里不能直接配置 type 为 img,因为目前是基于JSON Schema 规范,type 类型是不能有 img

所以推荐的解决方案,是封装你自己的upload,可以包含你的 默认图片上传地址包含输入框 等你需要自定义的内容

并且配置也会像下面这样简单,这样也不会打破 JSON Schema 规范

"imgUrl": {
    "title": "单个图片",
    "type": "string",
    "ui:widget": "YourUploadImg" // 自定义你的上传图片
}

// 注册一个全局组件
Vue.component('YourUploadImg', {
 render(h) {
  return h('a-upload', {
   ...
  })
 }
})

提议2

上面已经提到了,可以封装到你的 upload 组件中,比如直接使用现有的 UploadWidget ,那可以直接对组件加一个默认prop

Vue.component('my-upload', {
  functional: true,
  render: function (h, context) {
    return h(
      'UploadWidget',
      {
        ...context.data,
        props: {
          action: 'https://www..w.w.w/upload',
          ...context.data.props
        }
      },
      context.children
    )
  }
})

@jobyrao
Copy link
Author

jobyrao commented Mar 22, 2021

写了个自定义widget,此时该值如何修改到 formData.imgUrl ?

"imgUrl": {
    "title": "单个图片",
    "type": "string",
    "ui:widget": "JsonSchemaImageWidget"
}
<template>
  <div>
    <a-upload
        name="avatar"
        list-type="picture-card"
        class="avatar-uploader"
        :show-upload-list="false"
        :beforeUpload="uploadFile"
    >
      <img style="width: 100%" v-if="image" :src="image" alt="avatar" />
      <div v-else>
        <loading-outlined v-if="loading" />
        <plus-outlined v-else />
        <div class="ant-upload-text">Upload</div>
      </div>
    </a-upload>
    <a-input :value="image" @change="changeImage" />

  </div>
</template>
<script>
import {
  PlusOutlined,
  LoadingOutlined,
} from '@ant-design/icons-vue';
import {ref} from 'vue'
import {putCOS} from "@/services/common";

export default {
  name: 'JsonSchemaImageWidget',
  props: {
  },
  components: {
    PlusOutlined,
    LoadingOutlined,
  },
  setup(props) {
    const image = ref('');
    const loading = ref(false);

    const changeImage = (e) => {
      image.value = e.target.value
    }

    const uploadFile = async(e) => {
      loading.value = true;

      const res = await putCOS(e);
      image.value = res.location;

      loading.value = false;
      return Promise.reject();
    }

    return {
      image,
      changeImage,
      uploadFile,
      loading
    }
  }
};
</script>

image

@lljj-x
Copy link
Owner

lljj-x commented Mar 23, 2021

Widget 组件都必须是 v-model 的,并且在vue3中都使用modelValue props

<template>
    <div>
        <a-upload
            name="avatar"
            list-type="picture-card"
            class="avatar-uploader"
            :show-upload-list="false"
            :before-upload="uploadFile"
        >
            <img
                v-if="image"
                style="width: 100%"
                :src="image"
                alt="avatar"
            />
            <div v-else>
                <loading-outlined v-if="loading" />
                <plus-outlined v-else />
                <div class="ant-upload-text">Upload</div>
            </div>
        </a-upload>
        <a-input
            :value="image"
            @change="changeImage"
        />
    </div>
</template>
<script>
import {
    PlusOutlined,
    LoadingOutlined,
} from '@ant-design/icons-vue';
import { ref, computed } from 'vue';

export default {
    name: 'JsonSchemaImageWidget',
    components: {
        PlusOutlined,
        LoadingOutlined,
    },
    props: {
        modelValue: {
            type: String,
            default: null
        }
    },
    setup(props, { emit }) {
        const loading = ref(false);

        const image = computed({
            get: () => props.modelValue,
            set: (val) => {
                emit('update:modelValue', val);
            },
        });

        const changeImage = (e) => {
            image.value = e.target.value;
        };

        const uploadFile = async (e) => {
            loading.value = true;

            image.value = 'https://ww.ww.w.w';

            loading.value = false;
            return Promise.resolve();
        };

        return {
            image,
            changeImage,
            uploadFile,
            loading
        };
    }
};
</script>

image

@jobyrao
Copy link
Author

jobyrao commented Mar 23, 2021

good, 有用。
另外,翻了下颜色选择器实现,用的原生,一旦选择了颜色没法清空。。

再定义了一个颜色选择器

<template>
  <div>
    <a-input style="width: 150px" :value="color" @change="changeColor">
      <template #addonAfter>
        <a-input style="width: 40px" type="color" :value="color" @change="changeColor" />
      </template>
    </a-input>

  </div>
</template>
<script>
import {computed} from 'vue'

export default {
  name: 'JsonSchemaColorPicker',
  props: {
    modelValue: {
      type: String,
      default: null
    }
  },
  setup(props, { emit }) {

    const color = computed({
      get: () => props.modelValue,
      set: (val) => {
        emit('update:modelValue', val);
      },
    });
    const changeColor = (e) => {
      color.value = e.target.value
    }

    return {
      color,
      changeColor,
    }
  }
};
</script>

image

@lljj-x
Copy link
Owner

lljj-x commented Mar 24, 2021

奈斯,vue3 antd 没有提供 color picker,所以就使用了原生的 ,这样挺好,到时候可以更新在后续版本默认的format color中 。


另外上面使用 a-input 这类组件时,一般是直接使用 v-model 的,不用像 react 手动 vaule + onChange

<template>
    <div>
        <a-input
            v-model:value="color"
            style="width: 150px"
        >
            <template #addonAfter>
                <a-input
                    v-model:value="color"
                    style="width: 40px"
                    type="color"
                />
            </template>
        </a-input>
    </div>
</template>
<script>
import { computed } from 'vue';

export default {
    name: 'JsonSchemaColorPicker',
    props: {
        modelValue: {
            type: String,
            default: null
        }
    },
    setup(props, { emit }) {
        const color = computed({
            get: () => props.modelValue,
            set: (val) => {
                emit('update:modelValue', val);
            },
        });
        return {
            color
        };
    }
};
</script>

@jobyrao jobyrao closed this as completed Mar 24, 2021
@lljj-x lljj-x mentioned this issue Mar 25, 2021
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants