UNPKG

@form-create/vant-designer

Version:

基于Vant的移动端低代码可视化表单设计器,可以通过拖拽的方式快速创建表单,提高开发者对表单的开发效率。

490 lines (434 loc) 13.7 kB
import is, {hasProperty} from '@form-create/utils/lib/type'; import {parseFn} from '@form-create/utils/lib/json'; import toCase from '@form-create/utils/lib/tocase'; import {computed, isRef, ref, unref} from 'vue'; import ZhCn from '../locale/zh-cn'; import {message} from './message'; import {copy} from '@form-create/utils/lib/extend'; export {mobileTemplate, mobileTemplateV3, htmlTemplate} from './template'; export function makeRequiredRule() { return { type: 'Required', field: 'formCreate$required', title: '是否必填' }; } export function addAutoKeyMap(cm) { } export function makeTreeOptions(pre, config, level, data = []) { if (!config.id) { config.id = 1; } level && level--; for (let i = 0; i < 3; i++) { const item = { [config.label]: pre + level * 10 + (i + 1), [config.value]: '' + config.id++, }; if (level) { makeTreeOptions(pre, config, level, item.children = []); } data.push(item); } return data; } export const makeTitleRule = () => { return [ { type: 'switch', field: 'formCreateNative', props: { activeValue: false, inactiveValue: true, }, control: [{value: false, rule: ['formCreateTitle']}] }, { type: 'input', field: 'formCreateTitle', }, ] } export function makeOptionsRule(t, to) { const options = [ {'label': t('fetch.optionsType.struct'), 'value': 2}, {'label': t('fetch.optionsType.fetch'), 'value': 1}, ]; const control = [ { value: 1, rule: [ { type: 'FetchConfig', field: 'formCreateEffect>fetch', props: { to } } ] }, { value: 2, rule: [ { type: 'TableOptions', field: 'formCreate' + upper(to).replace('.', '>'), props: { column: [{label: t('props.key'), key: 'text'}, { value: true, label: t('props.value'), key: 'value' }], keyValue: 'text', } }, ], } ]; return { type: 'radio', title: t('props.options'), field: '_optionType', value: 2, options, props: { type: 'button' }, control }; } export function makeTreeOptionsRule(t, to, label, value) { const options = [ {'label': t('fetch.optionsType.struct'), 'value': 2}, {'label': t('fetch.optionsType.fetch'), 'value': 1}, ]; const control = [ { value: 1, rule: [ { type: 'FetchConfig', field: 'formCreateEffect>fetch', props: { to } } ] }, { value: 2, rule: [ { type: 'TreeOptions', field: 'formCreate' + upper(to).replace('.', '>'), props: { columns: { label, value }, keyValue: label, } }, ], } ]; return { type: 'radio', title: t('props.options'), field: '_optionType', value: 2, options, props: { type: 'button' }, control }; } export function upper(str) { return str.replace(str[0], str[0].toLocaleUpperCase()); } export const toJSON = function (val) { const type = /object ([a-zA-Z]*)/.exec(Object.prototype.toString.call(val)); if (type && _toJSON[type[1].toLowerCase()]) { return _toJSON[type[1].toLowerCase()](val); } else { return val; } }; const _toJSON = { object: function (val) { var json = []; for (var i in val) { if (!hasProperty(val, i)) continue; json.push( toJSON(i) + ': ' + ((val[i] != null) ? toJSON(val[i]) : 'null') ); } return '{\n ' + json.join(',\n ') + '\n}'; }, function: function (val) { val = '' + val; var exec = (/^ *([\w]+) *\(/).exec(val); if (exec && exec[1] !== 'function') { return 'function ' + val; } return val; }, array: function (val) { for (var i = 0, json = []; i < val.length; i++) json[i] = (val[i] != null) ? toJSON(val[i]) : 'null'; return '[' + json.join(', ') + ']'; }, string: function (val) { var tmp = val.split(''); for (var i = 0; i < tmp.length; i++) { var c = tmp[i]; (c >= ' ') ? (c === '\\') ? (tmp[i] = '\\\\') : (c === '"') ? (tmp[i] = '\\"') : 0 : (tmp[i] = (c === '\n') ? '\\n' : (c === '\r') ? '\\r' : (c === '\t') ? '\\t' : (c === '\b') ? '\\b' : (c === '\f') ? '\\f' : (c = c.charCodeAt(), ('\\u00' + ((c > 15) ? 1 : 0) + (c % 16))) ); } return '"' + tmp.join('') + '"'; } }; export const deepParseFn = function (target) { if (target && typeof target === 'object') { for (let key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { let data = target[key]; if (Array.isArray(data) || is.Object(data)) { deepParseFn(data); } if (is.String(data)) { target[key] = parseFn(data); } } } } return target; }; export function deepGet(object, path, defaultValue) { path = (path || '').split('.'); let index = 0, length = path.length; while (object != null && index < length) { object = object[path[index++]]; } return (index && index === length) ? (object !== undefined ? object : defaultValue) : defaultValue; } export const buildTranslator = (locale) => (path, option) => translate(path, option, unref(locale)); export const translate = (path, option, locale) => deepGet(locale, path, '').replace( /\{(\w+)\}/g, (_, key) => `${option?.[key] ?? `{${key}}`}` ) export const buildLocaleContext = (locale) => { const lang = computed(() => unref(locale).name) const name = computed(() => upper(toCase(lang.value || ''))) const localeRef = isRef(locale) ? locale : ref(locale) return { lang, name, locale: localeRef, t: buildTranslator(locale), } } export const useLocale = (locale) => { return buildLocaleContext(computed(() => locale.value || ZhCn)) } export const localeOptions = (t, options, prefix) => { return options.map(opt => { opt.label = t((prefix || 'props') + '.' + opt.label || opt.value) || opt.label; return opt; }) } export const localeProps = (t, prefix, rules) => { return rules.map(rule => { if (rule.field === 'formCreate$required') { rule.title = t('validate.required') || rule.title; } else if (rule.field && rule.field !== '_optionType') { rule.title = t('com.' + prefix + '.' + rule.field) || rule.title; } if (rule.type === 'template' && is.trueArray(rule.children)) { rule.children = localeProps(t, prefix, rule.children); } return rule; }) } export const getRuleTree = (children) => { const tree = []; children && children.forEach(rule => { if (rule._fc_drag_tag) { const item = { id: rule.__fc__.id, rule, children: getRuleTree(rule.children), }; if (!item.children.length) { delete item.children; } tree.push(item); } else { tree.push(...getRuleTree(rule.children)); } }); return tree; } export const getFormRuleDescription = (tree) => { const getTree = (children) => { const tree = []; children && children.forEach(rule => { if (rule.field) { rule.children = getTree(rule.children || []); if (!rule.children.length) { delete rule.children; } tree.push(rule); } else { tree.push(...getTree(rule.children || [])); } }); return tree; } return getTree(tree); }; export const getRuleDescription = (children) => { const getTree = (children) => { const tree = []; children && children.forEach(rule => { if (typeof rule !== 'object') { return; } if (rule._fc_drag_tag) { const item = { _fc_id: rule._fc_id, type: rule.type, field: rule.field, title: rule.title, name: rule.name, slot: rule.slot, props: {...rule.props || {}}, options: copy(rule.options), children: getTree(rule.children || []) }; if (rule.children && typeof rule.children[0] === 'string') { item.content = rule.children[0]; } if (!item.children.length) { delete item.children; } tree.push(item); } else { tree.push(...getTree(rule.children)); } }); return tree; } return getTree(children); }; export function getInjectArg(t) { return { name: '$inject', info: `$inject <a href="https://view.form-create.com/inject" target="_blank">${t('form.document')}</a>`, } } export function isElementInside(x, y, element) { const rect = element.getBoundingClientRect(); return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; } export function isNull(v) { return ['', null, undefined].indexOf(v) !== -1; } export function escapeRegExp(str) { return str.replace(/[\ .*+?^${}()|[\]\\]/g, '\\$&'); } export function compareVersion(v1, v2) { const a1 = v1.split('.'); const a2 = v2.split('.'); const minLength = Math.min(a1.length, a2.length); for (var i = 0; i < minLength; i++) { var diff = parseInt(a1[i], 10) - parseInt(a2[i], 10); if (diff > 0) { return 1; } else if (diff < 0) { return -1; } } return a1.length === a2.length ? 0 : (a1.length < a2.length ? -1 : 1); } export function copyTextToClipboard(text) { const textArea = document.createElement('textarea'); textArea.style.position = 'fixed'; textArea.style.top = 0; textArea.style.left = '-9999px'; textArea.value = text; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); } catch (err) { console.log('Oops, unable to copy'); } message('已复制!', 'success'); document.body.removeChild(textArea); } export function uniqueArray(arr) { return arr.filter((item, index) => arr.indexOf(item) === index); } /** * 节流函数 - 在指定时间内只执行一次函数 * @param {Function} func 要节流的函数 * @param {number} delay 延迟时间(毫秒) * @param {Object} options 选项 * @param {boolean} options.leading 是否在开始时执行 * @param {boolean} options.trailing 是否在结束时执行 * @returns {Function} 节流后的函数 */ export function throttle(func, delay, options = {}) { const {leading = false, trailing = true} = options; let timeout; let previous = 0; let result; let lastArgs; let lastContext; const throttled = function (...args) { const now = Date.now(); const context = this; // 保存最后一次调用的参数和上下文 lastArgs = args; lastContext = context; // 如果是第一次调用且不需要在开始时执行 if (!previous && !leading) { previous = now; } const remaining = delay - (now - previous); if (remaining <= 0 || remaining > delay) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(lastContext, lastArgs); } else if (!timeout && trailing) { timeout = setTimeout(() => { previous = leading ? 0 : Date.now(); timeout = null; result = func.apply(lastContext, lastArgs); }, remaining); } return result; }; throttled.cancel = function () { clearTimeout(timeout); previous = 0; timeout = null; lastArgs = null; lastContext = null; }; return throttled; }