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 | 2 | import App from '@/App.vue'; |
| 3 | 3 | import router from '@/router'; |
| 4 | 4 | import store from '@/store'; |
| 5 | +import request from '@/utils/request'; | |
| 5 | 6 | import ElementUI from 'element-ui'; |
| 6 | 7 | import Zee from '../packages'; |
| 7 | 8 | import NProgress from 'nprogress'; |
| ... | ... | @@ -18,8 +19,12 @@ Vue.component('code-snippet', CodeSnippet); |
| 18 | 19 | // 注册饿了么UI |
| 19 | 20 | Vue.use(ElementUI); |
| 20 | 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 | 28 | Vue.config.productionTip = false; |
| 24 | 29 | |
| 25 | 30 | new Vue({ | ... | ... |
| ... | ... | @@ -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 @@ |
| 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 @@ |
| 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 | 10 | |
| 11 | 11 | ```html |
| 12 | 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 | 14 | <el-table-column type="selection" align="center" width="40"></el-table-column> |
| 15 | 15 | <template #header> |
| 16 | 16 | <el-tabs v-model="activeName"> |
| ... | ... | @@ -63,7 +63,7 @@ export default { |
| 63 | 63 | { id: '8', name: '卢纳', age: 55 }, |
| 64 | 64 | ] |
| 65 | 65 | resolve({ |
| 66 | - result: params.currentPage === 1 ? list.slice(0, 5) : list.slice(5), | |
| 66 | + result: list, | |
| 67 | 67 | totalCount: list.length |
| 68 | 68 | }); |
| 69 | 69 | }, 1500); |
| ... | ... | @@ -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 | 137 | ## API |
| 105 | 138 | |
| 106 | 139 | ## Attribute 属性 | ... | ... |
| ... | ... | @@ -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 @@ |
| 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 | 36 | </component> |
| 37 | 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 | 82 | </template> |
| 80 | 83 | </component> |
| 81 | 84 | </template> |
| ... | ... | @@ -190,6 +193,19 @@ export default { |
| 190 | 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 | 211 | </script> | ... | ... |
packages/form/index.vue
packages/form/util.js
| ... | ... | @@ -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 | 14 | // 配置组件名称 |
| 15 | 15 | const name = prefix + component.name; |
| 16 | 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 | 26 | // 给每个子组件配置install方法 |
| 18 | 27 | component.install = function(Vue) { |
| 19 | 28 | Vue.component(name, component); | ... | ... |
| ... | ... | @@ -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 | 73 | \ No newline at end of file | ... | ... |
packages/scheme/index.vue
| 1 | 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 | 3 | </style> |
| 73 | 4 | |
| 74 | 5 | <template> |
| ... | ... | @@ -77,7 +8,7 @@ |
| 77 | 8 | <slot name="header" :filterModel="filterModel" v-bind="_slotScope"></slot> |
| 78 | 9 | </div> |
| 79 | 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 | 12 | </div> |
| 82 | 13 | <div v-if="action" class="zee-scheme__action"> |
| 83 | 14 | <slot v-if="hadSlot('action')" name="action" v-bind="_slotScope"></slot> |
| ... | ... | @@ -91,7 +22,7 @@ |
| 91 | 22 | ref="table" |
| 92 | 23 | v-model="tableData" |
| 93 | 24 | v-loading="loading" |
| 94 | - :list="list" | |
| 25 | + :list="listMap.table" | |
| 95 | 26 | :tableProps="{ border: true, 'row-key': 'id', ...tableProps }" |
| 96 | 27 | :size="size" |
| 97 | 28 | @selection-change="onTableSelectionChange" |
| ... | ... | @@ -135,7 +66,7 @@ |
| 135 | 66 | <slot v-if="hadSlot('dialog-title')" slot="title" name="dialog-title" :dialogType="dialogType" v-bind="_slotScope"></slot> |
| 136 | 67 | <template v-if="dialogRender"> |
| 137 | 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 | 70 | <div class="zee-scheme__dialog-button" v-if="['new', 'edit'].includes(dialogType)"> |
| 140 | 71 | <el-button :size="size" type="primary" @click="handleConfirm" :loading="submitting">确定</el-button> |
| 141 | 72 | <el-button :size="size" plain @click="closeDialog">取消</el-button> |
| ... | ... | @@ -147,7 +78,8 @@ |
| 147 | 78 | </template> |
| 148 | 79 | |
| 149 | 80 | <script> |
| 150 | -import { cloneDeep } from '../form/util'; | |
| 81 | +import { cloneDeep } from '../_utils'; | |
| 82 | +import { clear } from '../_utils/param'; | |
| 151 | 83 | |
| 152 | 84 | export default { |
| 153 | 85 | name: 'Scheme', |
| ... | ... | @@ -177,10 +109,17 @@ export default { |
| 177 | 109 | filterModel: Object, |
| 178 | 110 | auto: Boolean, |
| 179 | 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 | 124 | data() { |
| 186 | 125 | return { |
| ... | ... | @@ -192,11 +131,9 @@ export default { |
| 192 | 131 | dialogLoading: false, |
| 193 | 132 | dialogTitle: '', |
| 194 | 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 | 137 | tableData: [], |
| 201 | 138 | submitting: false, |
| 202 | 139 | loading: false, |
| ... | ... | @@ -209,6 +146,7 @@ export default { |
| 209 | 146 | } |
| 210 | 147 | }, |
| 211 | 148 | filters: { |
| 149 | + // 无规则过滤器,过滤掉筛选条件表单中的必填规则等 | |
| 212 | 150 | noRulesFilter(val = []) { |
| 213 | 151 | let list = cloneDeep(val); |
| 214 | 152 | const clearRules = list => { |
| ... | ... | @@ -225,6 +163,42 @@ export default { |
| 225 | 163 | }, |
| 226 | 164 | }, |
| 227 | 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 | 202 | _filterModel() { |
| 229 | 203 | return this.filterModel || this.filterForm || {}; |
| 230 | 204 | }, |
| ... | ... | @@ -237,15 +211,21 @@ export default { |
| 237 | 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 | 223 | methods: { |
| 242 | 224 | // 空Promise |
| 243 | 225 | emptyPromise() { |
| 244 | - return new Promise(resolve => { | |
| 245 | - resolve(); | |
| 246 | - }); | |
| 226 | + return new Promise(resolve => resolve()); | |
| 247 | 227 | }, |
| 248 | - // 设置第二行选中 | |
| 228 | + // 设置表格选中行 | |
| 249 | 229 | toggleRowSelection() { |
| 250 | 230 | this.tableData.forEach(row => { |
| 251 | 231 | if (this.selection.find(item => item.id === row.id)) { |
| ... | ... | @@ -282,6 +262,14 @@ export default { |
| 282 | 262 | this.$refs.table && this.$refs.table.clearSelection(); |
| 283 | 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 | 274 | async search() { |
| 287 | 275 | this.loading = true; |
| ... | ... | @@ -290,18 +278,16 @@ export default { |
| 290 | 278 | currentPage: this.currentPage, |
| 291 | 279 | pageSize: this.pageSize, |
| 292 | 280 | }; |
| 293 | - const searchAPI = this.searchAPI || this.emptyPromise; | |
| 281 | + const searchAPI = this.searchApi || this._searchAPI || this.emptyPromise; | |
| 294 | 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 | 289 | .catch(() => { |
| 304 | - this.$message.error('失败'); | |
| 290 | + this.$message.error('查询失败'); | |
| 305 | 291 | }); |
| 306 | 292 | this.loading = false; |
| 307 | 293 | }, |
| ... | ... | @@ -315,19 +301,43 @@ export default { |
| 315 | 301 | this.editForm = val || {}; |
| 316 | 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 | 321 | async onFormValidate(valid, model) { |
| 320 | 322 | if (valid) { |
| 321 | 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 | 331 | .then(() => { |
| 332 | + this.$message.success('保存成功'); | |
| 325 | 333 | this.closeDialog(); |
| 326 | 334 | }) |
| 327 | 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 | 356 | openNew() { |
| 347 | 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 | 371 | this.dialogLoading = true; |
| 352 | 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 | 396 | const loading = this.$loading({ |
| 366 | 397 | text: '处理中', |
| 367 | 398 | spinner: 'el-icon-loading', |
| 368 | 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 | 414 | openDialog(type, title) { | ... | ... |
packages/table/cell-editable.vue
packages/table/cell-value-render.js
packages/table/editable.vue
packages/table/index.vue