Commit 441f73b946ac046545f123693bf1215f48a1f5a6
1 parent
4f38f3a4
Exists in
master
and in
3 other branches
refactor: 重构Scheme支持分作用域及内外接口替换
Showing
17 changed files
with
647 additions
and
249 deletions
Show diff stats
examples/main.js
| @@ -2,6 +2,7 @@ import Vue from 'vue'; | @@ -2,6 +2,7 @@ import Vue from 'vue'; | ||
| 2 | import App from '@/App.vue'; | 2 | import App from '@/App.vue'; |
| 3 | import router from '@/router'; | 3 | import router from '@/router'; |
| 4 | import store from '@/store'; | 4 | import store from '@/store'; |
| 5 | +import request from '@/utils/request'; | ||
| 5 | import ElementUI from 'element-ui'; | 6 | import ElementUI from 'element-ui'; |
| 6 | import Zee from '../packages'; | 7 | import Zee from '../packages'; |
| 7 | import NProgress from 'nprogress'; | 8 | import NProgress from 'nprogress'; |
| @@ -18,8 +19,12 @@ Vue.component('code-snippet', CodeSnippet); | @@ -18,8 +19,12 @@ Vue.component('code-snippet', CodeSnippet); | ||
| 18 | // 注册饿了么UI | 19 | // 注册饿了么UI |
| 19 | Vue.use(ElementUI); | 20 | Vue.use(ElementUI); |
| 20 | // 注册Zee组件库 | 21 | // 注册Zee组件库 |
| 21 | -Vue.use(Zee); | 22 | +Vue.use(Zee, { |
| 23 | + alias: { list: 'result', total: 'totalCount' }, | ||
| 24 | + http: request, | ||
| 25 | +}); | ||
| 22 | 26 | ||
| 27 | +Vue.prototype.$http = request; | ||
| 23 | Vue.config.productionTip = false; | 28 | Vue.config.productionTip = false; |
| 24 | 29 | ||
| 25 | new Vue({ | 30 | new Vue({ |
| @@ -0,0 +1,86 @@ | @@ -0,0 +1,86 @@ | ||
| 1 | +const __DEBUG__ = process.env.NODE_ENV !== 'production'; | ||
| 2 | +/** | ||
| 3 | + * 缓存数据优化 | ||
| 4 | + * import cache from '@/utils/cache' | ||
| 5 | + * 使用方法 【 | ||
| 6 | + * 一、设置缓存 | ||
| 7 | + * string cache.put('k', 'string你好啊'); | ||
| 8 | + * json cache.put('k', { "b": "3" }, 2); | ||
| 9 | + * array cache.put('k', [1, 2, 3]); | ||
| 10 | + * boolean cache.put('k', true); | ||
| 11 | + * 二、读取缓存 | ||
| 12 | + * 默认值 cache.get('k') | ||
| 13 | + * string cache.get('k', '你好') | ||
| 14 | + * json cache.get('k', { "a": "1" }) | ||
| 15 | + * 三、移除/清理 | ||
| 16 | + * 移除: cache.remove('k'); | ||
| 17 | + * 清理:cache.clear(); | ||
| 18 | + * 】 | ||
| 19 | + * @type {String} | ||
| 20 | + */ | ||
| 21 | +const prefix = __DEBUG__ ? 'ZY_AUTH_DEV_' : 'ZY_AUTH_'; // 缓存前缀 | ||
| 22 | +const postfix = '_SEED'; | ||
| 23 | +/** | ||
| 24 | + * 设置缓存 | ||
| 25 | + * @param {[type]} k [键名] | ||
| 26 | + * @param {[type]} v [键值] | ||
| 27 | + * @param {[type]} t [时间、单位秒] | ||
| 28 | + */ | ||
| 29 | +function put(k, v, t) { | ||
| 30 | + localStorage.setItem(prefix + k, JSON.stringify(v)); | ||
| 31 | + var seconds = parseInt(t); | ||
| 32 | + if (seconds > 0) { | ||
| 33 | + var timestamp = Date.parse(new Date()); | ||
| 34 | + timestamp = timestamp / 1000 + seconds; | ||
| 35 | + localStorage.setItem(prefix + k + postfix, JSON.stringify(timestamp)); | ||
| 36 | + } else { | ||
| 37 | + localStorage.removeItem(prefix + k + postfix); | ||
| 38 | + } | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +/** | ||
| 42 | + * 获取缓存 | ||
| 43 | + * @param {[type]} k [键名] | ||
| 44 | + * @param {[type]} def [获取为空时默认] | ||
| 45 | + */ | ||
| 46 | +function get(k, def) { | ||
| 47 | + var deadtime = parseInt(localStorage.getItem(prefix + k + postfix)); | ||
| 48 | + if (deadtime) { | ||
| 49 | + if (parseInt(deadtime) < Date.parse(new Date()) / 1000) { | ||
| 50 | + if (def) { | ||
| 51 | + return def; | ||
| 52 | + } else { | ||
| 53 | + return false; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + var res = localStorage.getItem(prefix + k); | ||
| 58 | + if (res) { | ||
| 59 | + return JSON.parse(res); | ||
| 60 | + } else { | ||
| 61 | + if (def == undefined || def == '') { | ||
| 62 | + def = false; | ||
| 63 | + } | ||
| 64 | + return def; | ||
| 65 | + } | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +function remove(k) { | ||
| 69 | + localStorage.removeItem(prefix + k); | ||
| 70 | + localStorage.removeItem(prefix + k + postfix); | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +/** | ||
| 74 | + * 清理所有缓存 | ||
| 75 | + * @return {[type]} [description] | ||
| 76 | + */ | ||
| 77 | +function clear() { | ||
| 78 | + localStorage.clear(); | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +module.exports = { | ||
| 82 | + put: put, | ||
| 83 | + get: get, | ||
| 84 | + remove: remove, | ||
| 85 | + clear: clear, | ||
| 86 | +}; |
| @@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
| 1 | +export const stringify = json => { | ||
| 2 | + const urlEncode = (param, key, encode) => { | ||
| 3 | + if (param === null) return ''; | ||
| 4 | + let paramStr = ''; | ||
| 5 | + const t = typeof param; | ||
| 6 | + if (t === 'string' || t === 'number' || t === 'boolean') { | ||
| 7 | + paramStr = `&${key}=${encode === null || encode ? encodeURIComponent(param) : param}`; | ||
| 8 | + } else { | ||
| 9 | + for (const i in param) { | ||
| 10 | + if (i) { | ||
| 11 | + if (param[i] !== undefined && param[i] !== '' && !(param[i] && typeof param[i] === 'string' && /^\s+$/.test(param[i]))) { | ||
| 12 | + const k = key == null ? i : `${key}${param instanceof Array ? `[${i}]` : `.${i}`}`; | ||
| 13 | + paramStr += urlEncode(param[i], k, encode); | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + } | ||
| 17 | + } | ||
| 18 | + return paramStr; | ||
| 19 | + }; | ||
| 20 | + return urlEncode(json).substring(1); | ||
| 21 | +}; | ||
| 22 | + | ||
| 23 | +export const parse = url => { | ||
| 24 | + let obj = {}; // 创建一个Object | ||
| 25 | + let reg = /[?&][^?&]+=[^?&]+/g; // 正则匹配 ?&开始 =拼接 非?&结束 的参数 | ||
| 26 | + let arr = url.match(reg); // match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。 | ||
| 27 | + // arr数组形式 ['?id=12345','&a=b'] | ||
| 28 | + if (arr) { | ||
| 29 | + arr.forEach(item => { | ||
| 30 | + /** | ||
| 31 | + * tempArr数组 ['id','12345']和['a','b'] | ||
| 32 | + * 第一个是key,第二个是value | ||
| 33 | + * */ | ||
| 34 | + let tempArr = item.substring(1).split('='); | ||
| 35 | + let key = decodeURIComponent(tempArr[0]); | ||
| 36 | + let val = decodeURIComponent(tempArr[1]); | ||
| 37 | + obj[key] = val; | ||
| 38 | + }); | ||
| 39 | + } | ||
| 40 | + return obj; | ||
| 41 | +}; | ||
| 42 | + | ||
| 43 | +export const urlParam = data => { | ||
| 44 | + return `${data ? `?${stringify(data)}` : ''}`; | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +/** | ||
| 48 | + * 清除首尾斜杠,便于拼接字符串 | ||
| 49 | + * @param {*} str URL | ||
| 50 | + */ | ||
| 51 | +export const clear = str => { | ||
| 52 | + return str.replace(/^(\s|\/)+|(\s|\/)+$/g, ''); | ||
| 53 | +}; |
| @@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
| 1 | +import axios from 'axios'; | ||
| 2 | + | ||
| 3 | +const request = axios.create({ | ||
| 4 | + baseURL: 'http://47.98.245.217:7070/tms-web-api/', | ||
| 5 | + timeout: 1000 * 60, | ||
| 6 | + withCredentials: true, | ||
| 7 | + headers: { | ||
| 8 | + 'Content-Type': 'application/json; charset=utf-8', | ||
| 9 | + Authorization: 'Bearer cd355920-ff83-445e-87e1-39d7ecfb8566', | ||
| 10 | + }, | ||
| 11 | +}); | ||
| 12 | + | ||
| 13 | +// respone 拦截器 | ||
| 14 | +request.interceptors.response.use( | ||
| 15 | + response => { | ||
| 16 | + const { data = {}, config } = response; | ||
| 17 | + const { businessException, errorCode, message, success } = data; | ||
| 18 | + if (config && config.interceptors === false) { | ||
| 19 | + // 请求配置不做返回拦截的情况 | ||
| 20 | + return response; | ||
| 21 | + } else { | ||
| 22 | + if (success) { | ||
| 23 | + return data; | ||
| 24 | + } else { | ||
| 25 | + return Promise.reject(response); | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + }, | ||
| 29 | + error => { | ||
| 30 | + return Promise.reject(error); | ||
| 31 | + }, | ||
| 32 | +); | ||
| 33 | + | ||
| 34 | +export default request; |
examples/views/docs/component/scheme.md
| @@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
| 10 | 10 | ||
| 11 | ```html | 11 | ```html |
| 12 | <template> | 12 | <template> |
| 13 | - <z-scheme :list="list" :searchAPI="searchAPI" :getAPI="getAPI" :submitAPI="submitAPI" :deleteAPI="deleteAPI" auto real-selection> | 13 | + <z-scheme :list="list" :search-api="searchAPI" :get-api="getAPI" :submit-api="submitAPI" :delete-api="deleteAPI" auto real-selection> |
| 14 | <el-table-column type="selection" align="center" width="40"></el-table-column> | 14 | <el-table-column type="selection" align="center" width="40"></el-table-column> |
| 15 | <template #header> | 15 | <template #header> |
| 16 | <el-tabs v-model="activeName"> | 16 | <el-tabs v-model="activeName"> |
| @@ -63,7 +63,7 @@ export default { | @@ -63,7 +63,7 @@ export default { | ||
| 63 | { id: '8', name: '卢纳', age: 55 }, | 63 | { id: '8', name: '卢纳', age: 55 }, |
| 64 | ] | 64 | ] |
| 65 | resolve({ | 65 | resolve({ |
| 66 | - result: params.currentPage === 1 ? list.slice(0, 5) : list.slice(5), | 66 | + result: list, |
| 67 | totalCount: list.length | 67 | totalCount: list.length |
| 68 | }); | 68 | }); |
| 69 | }, 1500); | 69 | }, 1500); |
| @@ -101,6 +101,39 @@ export default { | @@ -101,6 +101,39 @@ export default { | ||
| 101 | 101 | ||
| 102 | ::: | 102 | ::: |
| 103 | 103 | ||
| 104 | +## 内置接口逻辑 | ||
| 105 | + | ||
| 106 | +如果CURD的接口都是同一路径下,可以使用内置接口逻辑快速对接 | ||
| 107 | + | ||
| 108 | +::: snippet 通过`url`配置接口路径,`http`设置Promise形式的HTTP请求库 | ||
| 109 | + | ||
| 110 | +```html | ||
| 111 | +<template> | ||
| 112 | + <z-scheme ref="scheme" :list="list" url="/customer" :http="$http" :alias="{ getUrl: '/getCustomerByCode', getKey: 'code' }" auto real-selection> | ||
| 113 | + <el-table-column type="selection" align="center" width="40"></el-table-column> | ||
| 114 | + </z-scheme> | ||
| 115 | +</template> | ||
| 116 | + | ||
| 117 | +<script> | ||
| 118 | +export default { | ||
| 119 | + data() { | ||
| 120 | + return { | ||
| 121 | + activeName: 'wait', | ||
| 122 | + list: [ | ||
| 123 | + { type: 'el-input', label: 'ID', key: 'id', props: { disabled: true }, include: 'form', visible: () => this.$refs.scheme.dialogType === 'edit' }, | ||
| 124 | + { type: 'el-input', label: '编号', key: 'code' }, | ||
| 125 | + { type: 'el-input', label: '名称', key: 'name' }, | ||
| 126 | + ] | ||
| 127 | + } | ||
| 128 | + }, | ||
| 129 | + methods: { | ||
| 130 | + } | ||
| 131 | +} | ||
| 132 | +</script> | ||
| 133 | +``` | ||
| 134 | + | ||
| 135 | +::: | ||
| 136 | + | ||
| 104 | ## API | 137 | ## API |
| 105 | 138 | ||
| 106 | ## Attribute 属性 | 139 | ## Attribute 属性 |
| @@ -0,0 +1,86 @@ | @@ -0,0 +1,86 @@ | ||
| 1 | +/** | ||
| 2 | + * 深度克隆对象 | ||
| 3 | + * @param {Object} obj 目标对象 | ||
| 4 | + * @returns {Object} 克隆的新对象 | ||
| 5 | + */ | ||
| 6 | +export const cloneDeep = obj => { | ||
| 7 | + if (typeof obj !== 'object') { | ||
| 8 | + return obj; | ||
| 9 | + } | ||
| 10 | + if (!obj) { | ||
| 11 | + return obj; | ||
| 12 | + } | ||
| 13 | + if (obj instanceof Date) { | ||
| 14 | + return new Date(obj); | ||
| 15 | + } | ||
| 16 | + if (obj instanceof RegExp) { | ||
| 17 | + return new RegExp(obj); | ||
| 18 | + } | ||
| 19 | + if (obj instanceof Function) { | ||
| 20 | + return obj; | ||
| 21 | + } | ||
| 22 | + let newObj; | ||
| 23 | + if (obj instanceof Array) { | ||
| 24 | + newObj = []; | ||
| 25 | + for (let i = 0, len = obj.length; i < len; i++) { | ||
| 26 | + newObj.push(cloneDeep(obj[i])); | ||
| 27 | + } | ||
| 28 | + return newObj; | ||
| 29 | + } | ||
| 30 | + newObj = {}; | ||
| 31 | + for (let key in obj) { | ||
| 32 | + if (Object.prototype.hasOwnProperty.call(obj, key)) { | ||
| 33 | + if (typeof obj[key] !== 'object') { | ||
| 34 | + newObj[key] = obj[key]; | ||
| 35 | + } else { | ||
| 36 | + newObj[key] = cloneDeep(obj[key]); | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + return newObj; | ||
| 41 | +}; | ||
| 42 | + | ||
| 43 | +/** | ||
| 44 | + * 对象深度取值 | ||
| 45 | + * @desctiption 来源于"typy.js"中src/util的getNestedObject函数 | ||
| 46 | + * @param {Object} obj 目标对象 | ||
| 47 | + * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的取值路径 | ||
| 48 | + * @example get({ a: { b: { c: ['d'] } } }, 'a.b.c.0') | ||
| 49 | + */ | ||
| 50 | +export const get = (obj, dotSeparatedKeys) => { | ||
| 51 | + if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined; | ||
| 52 | + if (typeof obj !== 'undefined' && typeof dotSeparatedKeys === 'string') { | ||
| 53 | + // eslint-disable-next-line no-useless-escape | ||
| 54 | + const splitRegex = /[.\[\]'"]/g; | ||
| 55 | + const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); | ||
| 56 | + obj = pathArr.reduce((o, key) => (o && o[key] !== undefined ? o[key] : undefined), obj); | ||
| 57 | + } | ||
| 58 | + return obj; | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +/** | ||
| 62 | + * 对象深度赋值 | ||
| 63 | + * @description 改写自get方法 | ||
| 64 | + * @param {Object} obj 目标对像 | ||
| 65 | + * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的赋值路径 | ||
| 66 | + * @param {*} value 目标值 | ||
| 67 | + * @example set(obj, 'a.b.c', 'd') | ||
| 68 | + */ | ||
| 69 | +export const set = (obj, dotSeparatedKeys, value) => { | ||
| 70 | + // eslint-disable-next-line no-useless-escape | ||
| 71 | + const splitRegex = /[.\[\]'"]/g; | ||
| 72 | + const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); | ||
| 73 | + const key = pathArr.pop(); | ||
| 74 | + pathArr.reduce((o, k) => { | ||
| 75 | + if ((o && o[k] === undefined) || o[k] === null) { | ||
| 76 | + o[k] = !isNaN(Number(key)) ? [] : {}; | ||
| 77 | + } | ||
| 78 | + return o[k]; | ||
| 79 | + }, obj)[key] = value; | ||
| 80 | +}; | ||
| 81 | + | ||
| 82 | +export default { | ||
| 83 | + cloneDeep, | ||
| 84 | + get, | ||
| 85 | + set, | ||
| 86 | +}; |
| @@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
| 1 | +export const stringify = json => { | ||
| 2 | + const urlEncode = (param, key, encode) => { | ||
| 3 | + if (param === null) return ''; | ||
| 4 | + let paramStr = ''; | ||
| 5 | + const t = typeof param; | ||
| 6 | + if (t === 'string' || t === 'number' || t === 'boolean') { | ||
| 7 | + paramStr = `&${key}=${encode === null || encode ? encodeURIComponent(param) : param}`; | ||
| 8 | + } else { | ||
| 9 | + for (const i in param) { | ||
| 10 | + if (i) { | ||
| 11 | + if (param[i] !== undefined && param[i] !== '' && !(param[i] && typeof param[i] === 'string' && /^\s+$/.test(param[i]))) { | ||
| 12 | + const k = key == null ? i : `${key}${param instanceof Array ? `[${i}]` : `.${i}`}`; | ||
| 13 | + paramStr += urlEncode(param[i], k, encode); | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + } | ||
| 17 | + } | ||
| 18 | + return paramStr; | ||
| 19 | + }; | ||
| 20 | + return urlEncode(json).substring(1); | ||
| 21 | +}; | ||
| 22 | + | ||
| 23 | +export const parse = url => { | ||
| 24 | + let obj = {}; // 创建一个Object | ||
| 25 | + let reg = /[?&][^?&]+=[^?&]+/g; // 正则匹配 ?&开始 =拼接 非?&结束 的参数 | ||
| 26 | + let arr = url.match(reg); // match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。 | ||
| 27 | + // arr数组形式 ['?id=12345','&a=b'] | ||
| 28 | + if (arr) { | ||
| 29 | + arr.forEach(item => { | ||
| 30 | + /** | ||
| 31 | + * tempArr数组 ['id','12345']和['a','b'] | ||
| 32 | + * 第一个是key,第二个是value | ||
| 33 | + * */ | ||
| 34 | + let tempArr = item.substring(1).split('='); | ||
| 35 | + let key = decodeURIComponent(tempArr[0]); | ||
| 36 | + let val = decodeURIComponent(tempArr[1]); | ||
| 37 | + obj[key] = val; | ||
| 38 | + }); | ||
| 39 | + } | ||
| 40 | + return obj; | ||
| 41 | +}; | ||
| 42 | + | ||
| 43 | +export const urlParam = data => { | ||
| 44 | + return `${data ? `?${stringify(data)}` : ''}`; | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +/** | ||
| 48 | + * 清除首尾斜杠,便于拼接字符串 | ||
| 49 | + * @param {*} str URL | ||
| 50 | + */ | ||
| 51 | +export const clear = str => { | ||
| 52 | + return str.replace(/^(\s|\/)+|(\s|\/)+$/g, ''); | ||
| 53 | +}; |
packages/form/form-render.vue
| @@ -36,46 +36,49 @@ | @@ -36,46 +36,49 @@ | ||
| 36 | </component> | 36 | </component> |
| 37 | </component> | 37 | </component> |
| 38 | <!-- 正常无分组表单项 --> | 38 | <!-- 正常无分组表单项 --> |
| 39 | - <component | ||
| 40 | - :is="colComponent" | ||
| 41 | - v-else | ||
| 42 | - :span="type === 'div' ? undefined : item.span || span" | ||
| 43 | - :key="index" | ||
| 44 | - :style="{ width: type === 'div' && item.style && item.style.width.includes('%') ? item.style.width : undefined, paddingRight: '10px' }" | ||
| 45 | - :class="colClassRender(item, index, colClass)" | ||
| 46 | - > | ||
| 47 | - <el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.fullKey" :rules="item.rules" :class="itemClass || 'zee-form__item'"> | ||
| 48 | - <slot v-if="$slots[item.fullKey]" :name="item.fullKey" :value="itemValue(item)" :model="value"></slot> | ||
| 49 | - <template v-else> | ||
| 50 | - <!-- 自定义组件 --> | ||
| 51 | - <dynamic-render | ||
| 52 | - v-if="typeof item.type === 'function'" | ||
| 53 | - :render=" | ||
| 54 | - item.type($createElement, { | ||
| 55 | - model: value, | ||
| 56 | - config: { | ||
| 57 | - props: { ...item.props, value: itemValue(item) }, | ||
| 58 | - style: item.style || { maxWidth: '100%' }, | ||
| 59 | - on: { | ||
| 60 | - ...bindItemEvent(item), | ||
| 61 | - input: v => onInput({ value: v, item }), | 39 | + <template v-else> |
| 40 | + <component | ||
| 41 | + :is="colComponent" | ||
| 42 | + v-if="bindItemVisible(item, 'visible')" | ||
| 43 | + v-show="bindItemVisible(item, 'show')" | ||
| 44 | + :span="type === 'div' ? undefined : item.span || span" | ||
| 45 | + :key="index" | ||
| 46 | + :style="{ width: type === 'div' && item.style && item.style.width.includes('%') ? item.style.width : undefined, paddingRight: '10px' }" | ||
| 47 | + :class="colClassRender(item, index, colClass)" | ||
| 48 | + > | ||
| 49 | + <el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.fullKey" :rules="item.rules" :class="itemClass || 'zee-form__item'"> | ||
| 50 | + <slot v-if="$slots[item.fullKey]" :name="item.fullKey" :value="itemValue(item)" :model="value"></slot> | ||
| 51 | + <template v-else> | ||
| 52 | + <!-- 自定义组件 --> | ||
| 53 | + <dynamic-render | ||
| 54 | + v-if="typeof item.type === 'function'" | ||
| 55 | + :render=" | ||
| 56 | + item.type($createElement, { | ||
| 57 | + model: value, | ||
| 58 | + config: { | ||
| 59 | + props: { ...item.props, value: itemValue(item) }, | ||
| 60 | + style: item.style || { maxWidth: '100%' }, | ||
| 61 | + on: { | ||
| 62 | + ...bindItemEvent(item), | ||
| 63 | + input: v => onInput({ value: v, item }), | ||
| 64 | + }, | ||
| 62 | }, | 65 | }, |
| 63 | - }, | ||
| 64 | - }) | ||
| 65 | - " | ||
| 66 | - ></dynamic-render> | ||
| 67 | - <component | ||
| 68 | - v-else | ||
| 69 | - :is="item.type" | ||
| 70 | - :value="itemValue(item)" | ||
| 71 | - @input="v => onInput({ value: v, item })" | ||
| 72 | - v-on="bindItemEvent(item)" | ||
| 73 | - v-bind="item.props" | ||
| 74 | - :style="item.style || { maxWidth: '100%' }" | ||
| 75 | - ></component> | ||
| 76 | - </template> | ||
| 77 | - </el-form-item> | ||
| 78 | - </component> | 66 | + }) |
| 67 | + " | ||
| 68 | + ></dynamic-render> | ||
| 69 | + <component | ||
| 70 | + v-else | ||
| 71 | + :is="item.type" | ||
| 72 | + :value="itemValue(item)" | ||
| 73 | + @input="v => onInput({ value: v, item })" | ||
| 74 | + v-on="bindItemEvent(item)" | ||
| 75 | + v-bind="item.props" | ||
| 76 | + :style="item.style || { maxWidth: '100%' }" | ||
| 77 | + ></component> | ||
| 78 | + </template> | ||
| 79 | + </el-form-item> | ||
| 80 | + </component> | ||
| 81 | + </template> | ||
| 79 | </template> | 82 | </template> |
| 80 | </component> | 83 | </component> |
| 81 | </template> | 84 | </template> |
| @@ -190,6 +193,19 @@ export default { | @@ -190,6 +193,19 @@ export default { | ||
| 190 | return undefined; | 193 | return undefined; |
| 191 | } | 194 | } |
| 192 | }, | 195 | }, |
| 196 | + /** | ||
| 197 | + * @description 绑定表单项显示状态 | ||
| 198 | + * @param {Object} item 表单项配置 | ||
| 199 | + * @param {String} type Vue显示类型,可选值:visible、show | ||
| 200 | + * @returns {Boolean} 显示状态 | ||
| 201 | + */ | ||
| 202 | + bindItemVisible(item, type) { | ||
| 203 | + const visible = item[type]; | ||
| 204 | + if (typeof visible === 'function') { | ||
| 205 | + return visible(this.model); | ||
| 206 | + } | ||
| 207 | + return item[type] !== false; | ||
| 208 | + }, | ||
| 193 | }, | 209 | }, |
| 194 | }; | 210 | }; |
| 195 | </script> | 211 | </script> |
packages/form/index.vue
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | 39 | ||
| 40 | <script> | 40 | <script> |
| 41 | import FormRender from './form-render'; | 41 | import FormRender from './form-render'; |
| 42 | -import { cloneDeep, set } from './util'; | 42 | +import { cloneDeep, set } from '../_utils'; |
| 43 | 43 | ||
| 44 | export default { | 44 | export default { |
| 45 | name: 'Form', | 45 | name: 'Form', |
packages/form/util.js
| @@ -1,86 +0,0 @@ | @@ -1,86 +0,0 @@ | ||
| 1 | -/** | ||
| 2 | - * 深度克隆对象 | ||
| 3 | - * @param {Object} obj 目标对象 | ||
| 4 | - * @returns {Object} 克隆的新对象 | ||
| 5 | - */ | ||
| 6 | -export const cloneDeep = obj => { | ||
| 7 | - if (typeof obj !== 'object') { | ||
| 8 | - return obj; | ||
| 9 | - } | ||
| 10 | - if (!obj) { | ||
| 11 | - return obj; | ||
| 12 | - } | ||
| 13 | - if (obj instanceof Date) { | ||
| 14 | - return new Date(obj); | ||
| 15 | - } | ||
| 16 | - if (obj instanceof RegExp) { | ||
| 17 | - return new RegExp(obj); | ||
| 18 | - } | ||
| 19 | - if (obj instanceof Function) { | ||
| 20 | - return obj; | ||
| 21 | - } | ||
| 22 | - let newObj; | ||
| 23 | - if (obj instanceof Array) { | ||
| 24 | - newObj = []; | ||
| 25 | - for (let i = 0, len = obj.length; i < len; i++) { | ||
| 26 | - newObj.push(cloneDeep(obj[i])); | ||
| 27 | - } | ||
| 28 | - return newObj; | ||
| 29 | - } | ||
| 30 | - newObj = {}; | ||
| 31 | - for (let key in obj) { | ||
| 32 | - if (Object.prototype.hasOwnProperty.call(obj, key)) { | ||
| 33 | - if (typeof obj[key] !== 'object') { | ||
| 34 | - newObj[key] = obj[key]; | ||
| 35 | - } else { | ||
| 36 | - newObj[key] = cloneDeep(obj[key]); | ||
| 37 | - } | ||
| 38 | - } | ||
| 39 | - } | ||
| 40 | - return newObj; | ||
| 41 | -}; | ||
| 42 | - | ||
| 43 | -/** | ||
| 44 | - * 对象深度取值 | ||
| 45 | - * @desctiption 来源于"typy.js"中src/util的getNestedObject函数 | ||
| 46 | - * @param {Object} obj 目标对象 | ||
| 47 | - * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的取值路径 | ||
| 48 | - * @example get({ a: { b: { c: ['d'] } } }, 'a.b.c.0') | ||
| 49 | - */ | ||
| 50 | -export const get = (obj, dotSeparatedKeys) => { | ||
| 51 | - if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined; | ||
| 52 | - if (typeof obj !== 'undefined' && typeof dotSeparatedKeys === 'string') { | ||
| 53 | - // eslint-disable-next-line no-useless-escape | ||
| 54 | - const splitRegex = /[.\[\]'"]/g; | ||
| 55 | - const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); | ||
| 56 | - obj = pathArr.reduce((o, key) => (o && o[key] !== undefined ? o[key] : undefined), obj); | ||
| 57 | - } | ||
| 58 | - return obj; | ||
| 59 | -}; | ||
| 60 | - | ||
| 61 | -/** | ||
| 62 | - * 对象深度赋值 | ||
| 63 | - * @description 改写自get方法 | ||
| 64 | - * @param {Object} obj 目标对像 | ||
| 65 | - * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的赋值路径 | ||
| 66 | - * @param {*} value 目标值 | ||
| 67 | - * @example set(obj, 'a.b.c', 'd') | ||
| 68 | - */ | ||
| 69 | -export const set = (obj, dotSeparatedKeys, value) => { | ||
| 70 | - // eslint-disable-next-line no-useless-escape | ||
| 71 | - const splitRegex = /[.\[\]'"]/g; | ||
| 72 | - const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); | ||
| 73 | - const key = pathArr.pop(); | ||
| 74 | - pathArr.reduce((o, k) => { | ||
| 75 | - if ((o && o[k] === undefined) || o[k] === null) { | ||
| 76 | - o[k] = !isNaN(Number(key)) ? [] : {}; | ||
| 77 | - } | ||
| 78 | - return o[k]; | ||
| 79 | - }, obj)[key] = value; | ||
| 80 | -}; | ||
| 81 | - | ||
| 82 | -export default { | ||
| 83 | - cloneDeep, | ||
| 84 | - get, | ||
| 85 | - set, | ||
| 86 | -}; |
packages/index.js
| @@ -14,6 +14,15 @@ const install = function(Vue, opts = {}) { | @@ -14,6 +14,15 @@ const install = function(Vue, opts = {}) { | ||
| 14 | // 配置组件名称 | 14 | // 配置组件名称 |
| 15 | const name = prefix + component.name; | 15 | const name = prefix + component.name; |
| 16 | component.name = name; | 16 | component.name = name; |
| 17 | + if (component.computed) { | ||
| 18 | + component.computed.zAlias = () => opts.alias || {}; | ||
| 19 | + component.computed.zHttp = () => opts.http; | ||
| 20 | + } else { | ||
| 21 | + component.computed = { | ||
| 22 | + zAlias: () => {}, | ||
| 23 | + zHttp: () => opts.http, | ||
| 24 | + }; | ||
| 25 | + } | ||
| 17 | // 给每个子组件配置install方法 | 26 | // 给每个子组件配置install方法 |
| 18 | component.install = function(Vue) { | 27 | component.install = function(Vue) { |
| 19 | Vue.component(name, component); | 28 | Vue.component(name, component); |
| @@ -0,0 +1,72 @@ | @@ -0,0 +1,72 @@ | ||
| 1 | +.zee-scheme { | ||
| 2 | + &__header { | ||
| 3 | + margin-bottom: 10px; | ||
| 4 | + } | ||
| 5 | + &__filter { | ||
| 6 | + border: 1px solid #ebeef5; | ||
| 7 | + padding-top: 10px; | ||
| 8 | + border-radius: 4px; | ||
| 9 | + margin-bottom: 10px; | ||
| 10 | + } | ||
| 11 | + &__action { | ||
| 12 | + display: flex; | ||
| 13 | + flex-wrap: wrap; | ||
| 14 | + align-items: center; | ||
| 15 | + justify-content: flex-start; | ||
| 16 | + line-height: 1; | ||
| 17 | + .el-button + .el-button { | ||
| 18 | + margin-left: 0; | ||
| 19 | + } | ||
| 20 | + .el-button { | ||
| 21 | + margin-right: 10px; | ||
| 22 | + margin-bottom: 10px; | ||
| 23 | + } | ||
| 24 | + } | ||
| 25 | + &__table { | ||
| 26 | + &-operation { | ||
| 27 | + display: flex; | ||
| 28 | + flex-wrap: wrap; | ||
| 29 | + align-items: center; | ||
| 30 | + justify-content: flex-start; | ||
| 31 | + .el-button + .el-button { | ||
| 32 | + margin-left: 0; | ||
| 33 | + } | ||
| 34 | + .el-button { | ||
| 35 | + margin-right: 10px; | ||
| 36 | + padding-top: 6px; | ||
| 37 | + padding-bottom: 6px; | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + &__dialog-button { | ||
| 42 | + display: flex; | ||
| 43 | + align-items: center; | ||
| 44 | + justify-content: center; | ||
| 45 | + padding-top: 10px; | ||
| 46 | + } | ||
| 47 | + &__footer { | ||
| 48 | + margin-top: 10px; | ||
| 49 | + text-align: right; | ||
| 50 | + display: flex; | ||
| 51 | + justify-content: space-between; | ||
| 52 | + align-items: center; | ||
| 53 | + .selection-info { | ||
| 54 | + word-break: break-all; | ||
| 55 | + white-space: nowrap; | ||
| 56 | + font-size: 12px; | ||
| 57 | + color: #606266; | ||
| 58 | + .num { | ||
| 59 | + color: #000; | ||
| 60 | + font-weight: bold; | ||
| 61 | + padding: 0 5px; | ||
| 62 | + font-size: 16px; | ||
| 63 | + } | ||
| 64 | + .el-button { | ||
| 65 | + margin-left: 5px; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + .el-pagination { | ||
| 69 | + flex: auto; | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | +} | ||
| 0 | \ No newline at end of file | 73 | \ No newline at end of file |
packages/scheme/index.vue
| 1 | <style lang="scss"> | 1 | <style lang="scss"> |
| 2 | -.zee-scheme { | ||
| 3 | - &__header { | ||
| 4 | - margin-bottom: 10px; | ||
| 5 | - } | ||
| 6 | - &__filter { | ||
| 7 | - border: 1px solid #ebeef5; | ||
| 8 | - padding-top: 10px; | ||
| 9 | - border-radius: 4px; | ||
| 10 | - margin-bottom: 10px; | ||
| 11 | - } | ||
| 12 | - &__action { | ||
| 13 | - display: flex; | ||
| 14 | - flex-wrap: wrap; | ||
| 15 | - align-items: center; | ||
| 16 | - justify-content: flex-start; | ||
| 17 | - line-height: 1; | ||
| 18 | - .el-button + .el-button { | ||
| 19 | - margin-left: 0; | ||
| 20 | - } | ||
| 21 | - .el-button { | ||
| 22 | - margin-right: 10px; | ||
| 23 | - margin-bottom: 10px; | ||
| 24 | - } | ||
| 25 | - } | ||
| 26 | - &__table { | ||
| 27 | - &-operation { | ||
| 28 | - display: flex; | ||
| 29 | - flex-wrap: wrap; | ||
| 30 | - align-items: center; | ||
| 31 | - justify-content: flex-start; | ||
| 32 | - .el-button + .el-button { | ||
| 33 | - margin-left: 0; | ||
| 34 | - } | ||
| 35 | - .el-button { | ||
| 36 | - margin-right: 10px; | ||
| 37 | - } | ||
| 38 | - } | ||
| 39 | - } | ||
| 40 | - &__dialog-button { | ||
| 41 | - display: flex; | ||
| 42 | - align-items: center; | ||
| 43 | - justify-content: center; | ||
| 44 | - padding-top: 10px; | ||
| 45 | - } | ||
| 46 | - &__footer { | ||
| 47 | - margin-top: 10px; | ||
| 48 | - text-align: right; | ||
| 49 | - display: flex; | ||
| 50 | - justify-content: space-between; | ||
| 51 | - align-items: center; | ||
| 52 | - .selection-info { | ||
| 53 | - word-break: break-all; | ||
| 54 | - white-space: nowrap; | ||
| 55 | - font-size: 12px; | ||
| 56 | - color: #606266; | ||
| 57 | - .num { | ||
| 58 | - color: #000; | ||
| 59 | - font-weight: bold; | ||
| 60 | - padding: 0 5px; | ||
| 61 | - font-size: 16px; | ||
| 62 | - } | ||
| 63 | - .el-button { | ||
| 64 | - margin-left: 5px; | ||
| 65 | - } | ||
| 66 | - } | ||
| 67 | - .el-pagination { | ||
| 68 | - flex: auto; | ||
| 69 | - } | ||
| 70 | - } | ||
| 71 | -} | 2 | +@import './index.scss'; |
| 72 | </style> | 3 | </style> |
| 73 | 4 | ||
| 74 | <template> | 5 | <template> |
| @@ -77,7 +8,7 @@ | @@ -77,7 +8,7 @@ | ||
| 77 | <slot name="header" :filterModel="filterModel" v-bind="_slotScope"></slot> | 8 | <slot name="header" :filterModel="filterModel" v-bind="_slotScope"></slot> |
| 78 | </div> | 9 | </div> |
| 79 | <div class="zee-scheme__filter"> | 10 | <div class="zee-scheme__filter"> |
| 80 | - <z-filter v-if="filter" :value="_filterModel" :list="list | noRulesFilter" :size="size" @input="onFilterInput" @search="search" :loading="loading"></z-filter> | 11 | + <z-filter v-if="filter" :value="_filterModel" :list="listMap.filter | noRulesFilter" :size="size" @input="onFilterInput" @search="search" :loading="loading"></z-filter> |
| 81 | </div> | 12 | </div> |
| 82 | <div v-if="action" class="zee-scheme__action"> | 13 | <div v-if="action" class="zee-scheme__action"> |
| 83 | <slot v-if="hadSlot('action')" name="action" v-bind="_slotScope"></slot> | 14 | <slot v-if="hadSlot('action')" name="action" v-bind="_slotScope"></slot> |
| @@ -91,7 +22,7 @@ | @@ -91,7 +22,7 @@ | ||
| 91 | ref="table" | 22 | ref="table" |
| 92 | v-model="tableData" | 23 | v-model="tableData" |
| 93 | v-loading="loading" | 24 | v-loading="loading" |
| 94 | - :list="list" | 25 | + :list="listMap.table" |
| 95 | :tableProps="{ border: true, 'row-key': 'id', ...tableProps }" | 26 | :tableProps="{ border: true, 'row-key': 'id', ...tableProps }" |
| 96 | :size="size" | 27 | :size="size" |
| 97 | @selection-change="onTableSelectionChange" | 28 | @selection-change="onTableSelectionChange" |
| @@ -135,7 +66,7 @@ | @@ -135,7 +66,7 @@ | ||
| 135 | <slot v-if="hadSlot('dialog-title')" slot="title" name="dialog-title" :dialogType="dialogType" v-bind="_slotScope"></slot> | 66 | <slot v-if="hadSlot('dialog-title')" slot="title" name="dialog-title" :dialogType="dialogType" v-bind="_slotScope"></slot> |
| 136 | <template v-if="dialogRender"> | 67 | <template v-if="dialogRender"> |
| 137 | <slot v-if="hadSlot(`dialog-${dialogType}`)" :name="`dialog-${dialogType}`" :model="_formModel" v-bind="_slotScope"></slot> | 68 | <slot v-if="hadSlot(`dialog-${dialogType}`)" :name="`dialog-${dialogType}`" :model="_formModel" v-bind="_slotScope"></slot> |
| 138 | - <z-form v-else ref="form" :value="_formModel" :list="list" label-width="80px" :span="12" @input="onFormInput" @validate="onFormValidate"></z-form> | 69 | + <z-form v-else ref="form" :value="_formModel" :list="listMap.form" label-width="80px" :span="12" @input="onFormInput" @validate="onFormValidate"></z-form> |
| 139 | <div class="zee-scheme__dialog-button" v-if="['new', 'edit'].includes(dialogType)"> | 70 | <div class="zee-scheme__dialog-button" v-if="['new', 'edit'].includes(dialogType)"> |
| 140 | <el-button :size="size" type="primary" @click="handleConfirm" :loading="submitting">确定</el-button> | 71 | <el-button :size="size" type="primary" @click="handleConfirm" :loading="submitting">确定</el-button> |
| 141 | <el-button :size="size" plain @click="closeDialog">取消</el-button> | 72 | <el-button :size="size" plain @click="closeDialog">取消</el-button> |
| @@ -147,7 +78,8 @@ | @@ -147,7 +78,8 @@ | ||
| 147 | </template> | 78 | </template> |
| 148 | 79 | ||
| 149 | <script> | 80 | <script> |
| 150 | -import { cloneDeep } from '../form/util'; | 81 | +import { cloneDeep } from '../_utils'; |
| 82 | +import { clear } from '../_utils/param'; | ||
| 151 | 83 | ||
| 152 | export default { | 84 | export default { |
| 153 | name: 'Scheme', | 85 | name: 'Scheme', |
| @@ -177,10 +109,17 @@ export default { | @@ -177,10 +109,17 @@ export default { | ||
| 177 | filterModel: Object, | 109 | filterModel: Object, |
| 178 | auto: Boolean, | 110 | auto: Boolean, |
| 179 | realSelection: Boolean, | 111 | realSelection: Boolean, |
| 180 | - searchAPI: Function, | ||
| 181 | - submitAPI: Function, | ||
| 182 | - getAPI: Function, | ||
| 183 | - deleteAPI: Function, | 112 | + /* 模板API */ |
| 113 | + url: String, // 请求地址 | ||
| 114 | + http: [Function, Promise], // http库 | ||
| 115 | + /* 自定义API */ | ||
| 116 | + searchApi: Function, // 搜索 | ||
| 117 | + submitApi: Function, // 提交 | ||
| 118 | + addApi: Function, // 新增 | ||
| 119 | + modifyApi: Function, // 修改 | ||
| 120 | + getApi: Function, // 查询详情 | ||
| 121 | + deleteApi: Function, // 删除 | ||
| 122 | + alias: Object, // 别名配置 | ||
| 184 | }, | 123 | }, |
| 185 | data() { | 124 | data() { |
| 186 | return { | 125 | return { |
| @@ -192,11 +131,9 @@ export default { | @@ -192,11 +131,9 @@ export default { | ||
| 192 | dialogLoading: false, | 131 | dialogLoading: false, |
| 193 | dialogTitle: '', | 132 | dialogTitle: '', |
| 194 | currentPage: 1, | 133 | currentPage: 1, |
| 195 | - // pageSize: 10, | ||
| 196 | - pageSize: 5, | ||
| 197 | - total: 127, | ||
| 198 | - // pageSizes: [10, 20, 50], | ||
| 199 | - pageSizes: [1, 2, 5], | 134 | + pageSize: 10, |
| 135 | + total: 0, | ||
| 136 | + pageSizes: [10, 20, 50], | ||
| 200 | tableData: [], | 137 | tableData: [], |
| 201 | submitting: false, | 138 | submitting: false, |
| 202 | loading: false, | 139 | loading: false, |
| @@ -209,6 +146,7 @@ export default { | @@ -209,6 +146,7 @@ export default { | ||
| 209 | } | 146 | } |
| 210 | }, | 147 | }, |
| 211 | filters: { | 148 | filters: { |
| 149 | + // 无规则过滤器,过滤掉筛选条件表单中的必填规则等 | ||
| 212 | noRulesFilter(val = []) { | 150 | noRulesFilter(val = []) { |
| 213 | let list = cloneDeep(val); | 151 | let list = cloneDeep(val); |
| 214 | const clearRules = list => { | 152 | const clearRules = list => { |
| @@ -225,6 +163,42 @@ export default { | @@ -225,6 +163,42 @@ export default { | ||
| 225 | }, | 163 | }, |
| 226 | }, | 164 | }, |
| 227 | computed: { | 165 | computed: { |
| 166 | + listMap() { | ||
| 167 | + // 默认作用域 | ||
| 168 | + const LIST_SPACE = ['filter', 'form', 'table']; | ||
| 169 | + const array = { | ||
| 170 | + filter: [], // 筛选 | ||
| 171 | + form: [], // 表单 | ||
| 172 | + table: [], // 表格 | ||
| 173 | + }; | ||
| 174 | + this.list.forEach(item => { | ||
| 175 | + // 可以在列表中通过include或exclude设置当前配置的作用域 | ||
| 176 | + const { include = LIST_SPACE, exclude = [] } = item; | ||
| 177 | + // 判断include | ||
| 178 | + let _inclue = []; | ||
| 179 | + if (include instanceof String || typeof include === 'string') { | ||
| 180 | + _inclue = [include]; | ||
| 181 | + } else if (include instanceof Array && typeof include === 'object') { | ||
| 182 | + _inclue = include; | ||
| 183 | + } | ||
| 184 | + // 判断exclude转换为include的情况 | ||
| 185 | + let _exclude_include = []; | ||
| 186 | + if (exclude instanceof String || typeof exclude === 'string') { | ||
| 187 | + _exclude_include = LIST_SPACE.filter(item => item !== exclude); | ||
| 188 | + } else if (exclude instanceof Array && typeof exclude === 'object') { | ||
| 189 | + _exclude_include = LIST_SPACE.filter(item => !exclude.includes(item)); | ||
| 190 | + } | ||
| 191 | + // 作用域交集 | ||
| 192 | + const _intersection = _inclue.filter(v => _exclude_include.includes(v)); | ||
| 193 | + // 返回改配置项的作用域 | ||
| 194 | + const _list_space = cloneDeep(_intersection); | ||
| 195 | + // 将配置项按需分配至各作用域下 | ||
| 196 | + _list_space.forEach(name => { | ||
| 197 | + array[name].push({ ...item, ...(item[name] || {}) }); | ||
| 198 | + }); | ||
| 199 | + }); | ||
| 200 | + return array; | ||
| 201 | + }, | ||
| 228 | _filterModel() { | 202 | _filterModel() { |
| 229 | return this.filterModel || this.filterForm || {}; | 203 | return this.filterModel || this.filterForm || {}; |
| 230 | }, | 204 | }, |
| @@ -237,15 +211,21 @@ export default { | @@ -237,15 +211,21 @@ export default { | ||
| 237 | closeDialog: this.closeDialog, | 211 | closeDialog: this.closeDialog, |
| 238 | }; | 212 | }; |
| 239 | }, | 213 | }, |
| 214 | + _alias() { | ||
| 215 | + const alias = this.alias; | ||
| 216 | + const zAlias = this.zAlias; | ||
| 217 | + if (alias && zAlias) { | ||
| 218 | + return { ...zAlias, ...alias }; | ||
| 219 | + } | ||
| 220 | + return this.alias || this.zAlias || {}; | ||
| 221 | + }, | ||
| 240 | }, | 222 | }, |
| 241 | methods: { | 223 | methods: { |
| 242 | // 空Promise | 224 | // 空Promise |
| 243 | emptyPromise() { | 225 | emptyPromise() { |
| 244 | - return new Promise(resolve => { | ||
| 245 | - resolve(); | ||
| 246 | - }); | 226 | + return new Promise(resolve => resolve()); |
| 247 | }, | 227 | }, |
| 248 | - // 设置第二行选中 | 228 | + // 设置表格选中行 |
| 249 | toggleRowSelection() { | 229 | toggleRowSelection() { |
| 250 | this.tableData.forEach(row => { | 230 | this.tableData.forEach(row => { |
| 251 | if (this.selection.find(item => item.id === row.id)) { | 231 | if (this.selection.find(item => item.id === row.id)) { |
| @@ -282,6 +262,14 @@ export default { | @@ -282,6 +262,14 @@ export default { | ||
| 282 | this.$refs.table && this.$refs.table.clearSelection(); | 262 | this.$refs.table && this.$refs.table.clearSelection(); |
| 283 | this.selection = []; | 263 | this.selection = []; |
| 284 | }, | 264 | }, |
| 265 | + // 内置搜索接口 | ||
| 266 | + _searchAPI(params) { | ||
| 267 | + if (this.url && (this.http || this.zHttp)) { | ||
| 268 | + const _http = this.http || this.zHttp; | ||
| 269 | + return _http({ url: `${clear(this.url)}/${this._alias.pageUrl || 'page'}`, params }); | ||
| 270 | + } | ||
| 271 | + return undefined; | ||
| 272 | + }, | ||
| 285 | // 搜索 | 273 | // 搜索 |
| 286 | async search() { | 274 | async search() { |
| 287 | this.loading = true; | 275 | this.loading = true; |
| @@ -290,18 +278,16 @@ export default { | @@ -290,18 +278,16 @@ export default { | ||
| 290 | currentPage: this.currentPage, | 278 | currentPage: this.currentPage, |
| 291 | pageSize: this.pageSize, | 279 | pageSize: this.pageSize, |
| 292 | }; | 280 | }; |
| 293 | - const searchAPI = this.searchAPI || this.emptyPromise; | 281 | + const searchAPI = this.searchApi || this._searchAPI || this.emptyPromise; |
| 294 | await searchAPI(params) | 282 | await searchAPI(params) |
| 295 | - .then(response => { | ||
| 296 | - const { result, totalCount } = response || {}; | ||
| 297 | - this.tableData = result; | ||
| 298 | - this.total = totalCount; | ||
| 299 | - this.$nextTick(() => { | ||
| 300 | - this.toggleRowSelection(); | ||
| 301 | - }); | 283 | + .then(res => { |
| 284 | + const response = res || {}; | ||
| 285 | + this.tableData = response[this._alias.list || 'list'] || []; | ||
| 286 | + this.total = response[this._alias.total || 'total'] || 0; | ||
| 287 | + this.$nextTick(this.toggleRowSelection); | ||
| 302 | }) | 288 | }) |
| 303 | .catch(() => { | 289 | .catch(() => { |
| 304 | - this.$message.error('失败'); | 290 | + this.$message.error('查询失败'); |
| 305 | }); | 291 | }); |
| 306 | this.loading = false; | 292 | this.loading = false; |
| 307 | }, | 293 | }, |
| @@ -315,19 +301,43 @@ export default { | @@ -315,19 +301,43 @@ export default { | ||
| 315 | this.editForm = val || {}; | 301 | this.editForm = val || {}; |
| 316 | this.$emit('update:formModel', val || {}); | 302 | this.$emit('update:formModel', val || {}); |
| 317 | }, | 303 | }, |
| 304 | + // 内置新增保存接口 | ||
| 305 | + _addAPI(data) { | ||
| 306 | + if (this.url && (this.http || this.zHttp)) { | ||
| 307 | + const _http = this.http || this.zHttp; | ||
| 308 | + return _http({ url: `${clear(this.url)}/${this._alias.addUrl || 'add'}`, method: 'post', data }); | ||
| 309 | + } | ||
| 310 | + return undefined; | ||
| 311 | + }, | ||
| 312 | + // 内置修改保存接口 | ||
| 313 | + _modifyAPI(data) { | ||
| 314 | + if (this.url && (this.http || this.zHttp)) { | ||
| 315 | + const _http = this.http || this.zHttp; | ||
| 316 | + return _http({ url: `${clear(this.url)}/${this._alias.modifyUrl || 'modify'}`, method: 'post', data }); | ||
| 317 | + } | ||
| 318 | + return undefined; | ||
| 319 | + }, | ||
| 318 | // 表单提交且通过校验 | 320 | // 表单提交且通过校验 |
| 319 | async onFormValidate(valid, model) { | 321 | async onFormValidate(valid, model) { |
| 320 | if (valid) { | 322 | if (valid) { |
| 321 | this.submitting = true; | 323 | this.submitting = true; |
| 322 | - const submitAPI = this.submitAPI || this.emptyPromise; | ||
| 323 | - await submitAPI(model, { type: this.dialogType }) | 324 | + let submitAPI = this.submitApi || this.emptyPromise; |
| 325 | + if (this.dialogType === 'new') { | ||
| 326 | + submitAPI = this.addApi || this.submitApi || this._addAPI || this.emptyPromise; | ||
| 327 | + } else if (this.dialogType === 'edit') { | ||
| 328 | + submitAPI = this.modifyApi || this.submitApi || this._modifyAPI || this.emptyPromise; | ||
| 329 | + } | ||
| 330 | + submitAPI(model, { type: this.dialogType }) | ||
| 324 | .then(() => { | 331 | .then(() => { |
| 332 | + this.$message.success('保存成功'); | ||
| 325 | this.closeDialog(); | 333 | this.closeDialog(); |
| 326 | }) | 334 | }) |
| 327 | .catch(() => { | 335 | .catch(() => { |
| 328 | - this.$message.error('失败'); | 336 | + this.$message.error('保存失败'); |
| 337 | + }) | ||
| 338 | + .finally(() => { | ||
| 339 | + this.submitting = false; | ||
| 329 | }); | 340 | }); |
| 330 | - this.submitting = false; | ||
| 331 | } | 341 | } |
| 332 | }, | 342 | }, |
| 333 | // 表单按钮确定 | 343 | // 表单按钮确定 |
| @@ -346,32 +356,59 @@ export default { | @@ -346,32 +356,59 @@ export default { | ||
| 346 | openNew() { | 356 | openNew() { |
| 347 | this.openDialog('new', '新增'); | 357 | this.openDialog('new', '新增'); |
| 348 | }, | 358 | }, |
| 359 | + // 内置查询详情接口 | ||
| 360 | + _getAPI(row) { | ||
| 361 | + if (this.url && (this.http || this.zHttp)) { | ||
| 362 | + const _http = this.http || this.zHttp; | ||
| 363 | + const _getKey = this._alias.getKey || this._alias.primaryKey || 'id'; | ||
| 364 | + const _resultKey = this._alias.result || 'result'; | ||
| 365 | + return _http({ url: `${clear(this.url)}/${this._alias.getUrl || 'queryById'}`, params: { [_getKey]: row[_getKey] } }).then(response => response[_resultKey] || {}); | ||
| 366 | + } | ||
| 367 | + return undefined; | ||
| 368 | + }, | ||
| 349 | // 打开编辑弹出框 | 369 | // 打开编辑弹出框 |
| 350 | - async openEdit({ row }) { | 370 | + openEdit({ row }) { |
| 351 | this.dialogLoading = true; | 371 | this.dialogLoading = true; |
| 352 | this.openDialog('edit', '编辑'); | 372 | this.openDialog('edit', '编辑'); |
| 353 | - const getRow = () => { | ||
| 354 | - this.editForm = { ...row }; | ||
| 355 | - }; | ||
| 356 | - const getAPI = this.getAPI || getRow; | ||
| 357 | - await getAPI(row).then(result => { | ||
| 358 | - this.editForm = { ...result }; | ||
| 359 | - }); | ||
| 360 | - this.dialogLoading = false; | 373 | + const getRow = () => |
| 374 | + new Promise(resolve => { | ||
| 375 | + resolve({ ...row }); | ||
| 376 | + }); | ||
| 377 | + const getAPI = this.getApi || this._getAPI || getRow; | ||
| 378 | + getAPI(row) | ||
| 379 | + .then(result => { | ||
| 380 | + this.editForm = { ...result }; | ||
| 381 | + }) | ||
| 382 | + .finally(() => { | ||
| 383 | + this.dialogLoading = false; | ||
| 384 | + }); | ||
| 385 | + }, | ||
| 386 | + // 内置删除接口 | ||
| 387 | + _deleteAPI(keys) { | ||
| 388 | + if (this.url && (this.http || this.zHttp)) { | ||
| 389 | + const _http = this.http || this.zHttp; | ||
| 390 | + return _http({ url: `${clear(this.url)}/${this._alias.modifyUrl || 'delete'}`, method: 'post', data: keys }); | ||
| 391 | + } | ||
| 392 | + return undefined; | ||
| 361 | }, | 393 | }, |
| 362 | // 删除 | 394 | // 删除 |
| 363 | - async handleDelete({ row, $index }) { | ||
| 364 | - const deleteAPI = this.deleteAPI || this.emptyPromise; | 395 | + handleDelete({ row, $index }) { |
| 365 | const loading = this.$loading({ | 396 | const loading = this.$loading({ |
| 366 | text: '处理中', | 397 | text: '处理中', |
| 367 | spinner: 'el-icon-loading', | 398 | spinner: 'el-icon-loading', |
| 368 | background: 'rgba(255, 255, 255, 0.5)', | 399 | background: 'rgba(255, 255, 255, 0.5)', |
| 369 | }); | 400 | }); |
| 370 | - await deleteAPI(row).then(() => { | ||
| 371 | - this.tableData.splice($index, 1); | ||
| 372 | - this.$message.success('删除成功'); | ||
| 373 | - }); | ||
| 374 | - loading.close(); | 401 | + const deleteAPI = this.deleteApi || this._deleteAPI || this.emptyPromise; |
| 402 | + const _deleteKey = this._alias.deleteKey || this._alias.primaryKey || 'id'; | ||
| 403 | + const keys = [row[_deleteKey]]; | ||
| 404 | + deleteAPI(keys) | ||
| 405 | + .then(() => { | ||
| 406 | + this.search(); | ||
| 407 | + this.$message.success('删除成功'); | ||
| 408 | + }) | ||
| 409 | + .finally(() => { | ||
| 410 | + loading.close(); | ||
| 411 | + }); | ||
| 375 | }, | 412 | }, |
| 376 | // 打开弹出框 | 413 | // 打开弹出框 |
| 377 | openDialog(type, title) { | 414 | openDialog(type, title) { |
packages/table/cell-editable.vue
packages/table/cell-value-render.js
packages/table/editable.vue
| @@ -70,7 +70,7 @@ | @@ -70,7 +70,7 @@ | ||
| 70 | </template> | 70 | </template> |
| 71 | 71 | ||
| 72 | <script> | 72 | <script> |
| 73 | -import { cloneDeep, get, set } from '../form/util'; | 73 | +import { cloneDeep, get, set } from '../_utils'; |
| 74 | import CellEditable from './cell-editable'; | 74 | import CellEditable from './cell-editable'; |
| 75 | import CellValueRender from './cell-value-render'; | 75 | import CellValueRender from './cell-value-render'; |
| 76 | 76 |
packages/table/index.vue
| @@ -53,7 +53,7 @@ | @@ -53,7 +53,7 @@ | ||
| 53 | </template> | 53 | </template> |
| 54 | 54 | ||
| 55 | <script> | 55 | <script> |
| 56 | -import { cloneDeep, get, set } from '../form/util'; | 56 | +import { cloneDeep, get, set } from '../_utils'; |
| 57 | import CellValueRender from './cell-value-render'; | 57 | import CellValueRender from './cell-value-render'; |
| 58 | 58 | ||
| 59 | export default { | 59 | export default { |