diff --git a/examples/router/routes.js b/examples/router/routes.js index 6aef0d3..1e70614 100644 --- a/examples/router/routes.js +++ b/examples/router/routes.js @@ -1,6 +1,7 @@ import DefaultLayout from '@/views/layout/default'; import ComponentLayout from '@/views/layout/component'; import DevelopLayout from '@/views/layout/develop'; +import DesignLayout from '@/views/layout/design'; // 开发指南的文档 @@ -89,10 +90,10 @@ const _components = [ component: () => import('@/views/docs/component/schema-table.md'), }, { - path: 'schema', - name: 'schema', - meta: { title: 'Schema 方案' }, - component: () => import('@/views/docs/component/schema.md'), + path: 'schema-page', + name: 'schema-page', + meta: { title: 'Schema Page 页面' }, + component: () => import('@/views/docs/component/schema-page.md'), }, ], }, @@ -139,24 +140,6 @@ const _develops = [ meta: { title: 'Schema 介绍' }, component: () => import('@/views/docs/develop/schema/introduce.md'), }, - { - path: 'schema-form', - name: 'developSchemaForm', - meta: { title: 'Schema Form 方案表单' }, - component: () => import('@/views/docs/develop/schema/schema-form.md'), - }, - { - path: 'schema-table', - name: 'developSchemaTable', - meta: { title: 'Schema Table 方案表格' }, - component: () => import('@/views/docs/develop/schema/schema-table.md'), - }, - { - path: 'schema-page', - name: 'developSchemaPage', - meta: { title: 'Schema Page 方案页面' }, - component: () => import('@/views/docs/develop/schema/schema-page.md'), - }, ], }, ]; @@ -166,6 +149,25 @@ _develops.forEach(data => { _develops_children = [..._develops_children, ...data.children]; }); +const _designs = [ + { + group: '表格', + children: [ + { + path: 'table', + name: 'designTable', + meta: { title: '表格设计规范' }, + component: () => import('@/views/docs/design/table.md'), + }, + ], + }, +]; + +let _designs_children = []; +_designs.forEach(data => { + _designs_children = [..._designs_children, ...data.children]; +}); + // 用于导航的页面 const _pages = [ { @@ -190,6 +192,14 @@ const _pages = [ children: [..._components_children, ..._guides, ..._others], }, { + path: '/design', + name: 'design', + meta: { title: '设计', path: '/design' }, + component: DesignLayout, + redirect: `/design/${_designs[0].children[0].path}`, + children: [..._designs_children], + }, + { path: '/develop', name: 'develop', meta: { title: '开发', path: '/develop' }, @@ -204,6 +214,7 @@ export const guides = _guides; export const components = _components; export const others = _others; export const develops = _develops; +export const designs = _designs; export default [ { path: '*', redirect: '/404', hidden: true }, diff --git a/examples/views/docs/component/schema-page.md b/examples/views/docs/component/schema-page.md new file mode 100644 index 0000000..1e6cea5 --- /dev/null +++ b/examples/views/docs/component/schema-page.md @@ -0,0 +1,573 @@ +# Schema Page 方案页面 + +根据JSON Schema配置自动生成一个包含搜索、表格、表单、详情功能的页面 + +## 基础用法 + +`schema`设置配置项,其中包括`filter`、`table`、`form`三个基本schema配置,配置方式分别对应`z-schema-filter`、`z-schema-table`、`z-schema-form`,绑定值则分别对应`value-filter`、`value-table`、`value-form`,由于是多个双向绑定的值,所以使用`sync`修饰符来做双向绑定。 + +::: snippet 基础示例 + +```html + + + +``` + +::: + +## 详情入口 + +可以通过自定义插槽的方式,在单元格内指定详情入口。由于三个子组件均支持自定义插槽,所以在本组件中使用自定义插槽时需要加上子组件**前缀**。 + +::: snippet 配置表格中的详情入口可以使用`table-cell-`插槽。 + +```html + + + +``` + +::: + +## 对接接口 + +本组件预置了增删改查逻辑,因此分别对应`api-search`、`api-new`、`api-edit`、`api-get`、`api-delete`五个基本接口。 + +::: snippet 接口格式为返回一个**Promise**对象的**Function**。其中,`api-search`的执行结果必须是{ list: [...], total: n }的格式,`api-get`的执行结果必须与`valur-form`相对应,`api-new`、`api-edit`、`api-delete`保持默认的**Promise**的resolve或reject逻辑即可,详情见示例。 + +```html + + + +``` + +::: + +## 详情接口 + +由于可能出现详情与表单不同的情况,因此本组件提供了`value-detail`属性和`api-detail`接口可独立维护详情页面,若详情表单项也不同,则可以在`schema`中配置`detail`来设置。 + +::: snippet 若没有详情绑定值属性和接口,则默认以表单的值为准。 + +```html + + + +``` + +::: + +## 弹窗类型 + +除了本组件内置的`new`、`edit`、`detail`三种弹出框模式之外,还可以通过任意插槽打开任意自定义弹出框。也支持重新定义原有的三种弹框,同时也需要重新自定义表单校验和提交等逻辑。 + +::: snippet 插槽提供`openDialog`打开弹出框方法,参数类型为(type: 弹出框类型, title: 弹出框标题, config: 弹出框配置),弹出框主体通过`dialog-`插槽定义。`closeDialog`为关闭弹出框。 + +```html + + + +``` + +::: + +## 按钮权限 + +本组件不包含自定义业务逻辑,因此配置项不包含权限判断,如果需要按钮的权限判断,可以通过`action`插槽和`operation`插槽将渲染逻辑暴露在视图模板中,然后进行自定义判断。 + +::: snippet 本示例项目中没有`v-permission`等自定义权限指令,使用时根据实际情况在对应的按钮加上判断即可 + +```html + + + +``` + +::: + +## API + +## Attribute 属性 + +参数|说明|类型|可选值|默认值 +-|-|-|-|- +schema | JSON Schema配置项列表 | Array | - | [] diff --git a/examples/views/docs/component/schema.md b/examples/views/docs/component/schema.md deleted file mode 100644 index 457d7eb..0000000 --- a/examples/views/docs/component/schema.md +++ /dev/null @@ -1,155 +0,0 @@ -# Schema 方案 - -根据JSON Schema配置自动生成一个包含搜索条件、表格、编辑表单、详情表单的页面 - -## 基础用法 - -通过配置`JSON Schema`快速生成CURD逻辑 - -::: snippet 通过`list`配置项目 - -```html - - - -``` - -::: - -## 内置接口逻辑 - -如果CURD的接口都是同一路径下,可以使用内置接口逻辑快速对接 - -::: snippet 通过`url`配置接口路径,`http`设置Promise形式的HTTP请求库 - -```html - - - -``` - -::: - -## API - -## Attribute 属性 - -参数|说明|类型|可选值|默认值 --|-|-|-|- -list | JSON Schema配置项列表 | Array | - | [] diff --git a/examples/views/docs/design/table.md b/examples/views/docs/design/table.md new file mode 100644 index 0000000..2b30c87 --- /dev/null +++ b/examples/views/docs/design/table.md @@ -0,0 +1,164 @@ + +# 表格设计规范 + +## 一、表头 + +### 对齐方式 + +文本信息左对齐,因为现代人的阅读方式习惯从左到右,符合正常的心智; + +数据信息右对齐,更加方便数字大小的直观对比; + +固定内容居中对齐,更好的信息呈现及表格空间的节省; + +表头与信息内容对齐方式一致,一致性以达到简化,降低视觉噪音。 + +### 排序 + +按照时间、名称降序排列 + +## 二、列 + +### 对齐方式 + +与表头对齐方式保持一致 + +### 列宽 + +默认不设置列宽,自动适配 + +时间YYYY-MM-DD HH:mm:ss,最小宽度140 + +### 数据截断 + +动态内容,无法确认宽度时,默认设置show-overflow-tooltip + +内容大于240时,必须设置show-overflow-tooltip + +### 详情入口 + +默认为第一列,如订单号、运单号、名称 + +特殊情况下可显示在操作列 + +### 展开行 + +列数较多且内容不便用表格展示时,可以设置展开行 + +## 三、单元格 + +### 默认文本 + +样式默认即可 + +使用插槽自定义时,进行空值处理,防止出现null、undefined + +### 组件 + +特点、标签类的内容,可以使用el-tag进行展示 + +布尔类型的数据,可以用el-tag或者红绿指示点,避免通篇显示是否 + +### 行内编辑 + +修改内容只有1个值时,使用行内编辑 + +修改内容大于1个值时,使用el-dialog或el-drawer + +### 快捷操作 + +频繁复制的内容,设置快速复制按钮 + +入口列的内容,使用el-link,表示可以点击跳转 + +行内编辑单元格,显示行内编辑操作按钮 + +## 四、操作列 + +### 默认项 + +默认悬浮固定在表格右侧 + +默认展示编辑、删除按钮 + +使用图标代替文字展示操作按钮,避免操作列因大量文字展示过长 + +设置图标时,必须设置title属性,鼠标悬停时显示说明内容 + +### 宽度 + +设置与当前操作行按钮数量总宽度相当,避免出现多余空位 + +出现过多操作按钮,保留使用频率最高的按钮,其余的显示为“更多”,下拉展示 + +### 展示方式 + +文本 + +图标 + +下拉列表 + +### 功能提示 + +设置title对当前功能进行描述 + +## 五、浮动列 + +### 表格选择列 + +宽度固定40 + +### 主要内容列 + +列数多时左侧固定 + +设置快捷操作,跳转入口、快速复制 + +## 六、表格设置 + +### 宽度 + +默认100%即可,不要设置固定宽度 + +### 高度 + +正常情况下,不设置表格的固定高度 + +最大高度以当前分页条数为准 + +### 边框 + +开启边框 + +### 斑马纹 + +不设置斑马纹 + +### 树形数据 + +包含层级的功能,如菜单、组织等功能,使用树形数据展示 + +## 七、业务数据 + +### 常规数字 + +右对齐 + +### 金额数字 + +千分位、保留两位小数、右对齐 + +### 时间展示 + +格式化YYYY-MM-DD HH:mm:ss + +### 类型标签 + +单个描述性特点时,使用el-tag + +多个同类描述性特点时,在单个列中同时展示多个el-tag + +### 活动指示器 + +布尔类型值,优先用指示器,避免直接显示“是”“否” diff --git a/examples/views/docs/develop/schema/schema-form.md b/examples/views/docs/develop/schema/schema-form.md deleted file mode 100644 index 9bf9c18..0000000 --- a/examples/views/docs/develop/schema/schema-form.md +++ /dev/null @@ -1,75 +0,0 @@ -# Schema Form 方案表单 - -通过配置JSON Schema的方式快速生成一个表单 - -## 基础用法 - -::: snippet 预置了许多业务逻辑,避免重复维护相同的业务逻辑 - -```html - - - -``` - -::: \ No newline at end of file diff --git a/examples/views/docs/develop/schema/schema-page.md b/examples/views/docs/develop/schema/schema-page.md deleted file mode 100644 index f76d90a..0000000 --- a/examples/views/docs/develop/schema/schema-page.md +++ /dev/null @@ -1,5 +0,0 @@ -# Schema 方案开发 - -通过配置JSON Schema的方式快速生成一些业务组件 - -## 基础用法 \ No newline at end of file diff --git a/examples/views/docs/develop/schema/schema-table.md b/examples/views/docs/develop/schema/schema-table.md deleted file mode 100644 index f76d90a..0000000 --- a/examples/views/docs/develop/schema/schema-table.md +++ /dev/null @@ -1,5 +0,0 @@ -# Schema 方案开发 - -通过配置JSON Schema的方式快速生成一些业务组件 - -## 基础用法 \ No newline at end of file diff --git a/examples/views/layout/design.vue b/examples/views/layout/design.vue new file mode 100644 index 0000000..d90a8db --- /dev/null +++ b/examples/views/layout/design.vue @@ -0,0 +1,36 @@ + + + diff --git a/packages/form-item/index.vue b/packages/form-item/index.vue index c38c583..2c4ef99 100644 --- a/packages/form-item/index.vue +++ b/packages/form-item/index.vue @@ -4,7 +4,7 @@ export default { props: { label: String, labelWidth: String, - value: [Number, String], + value: [Number, String, Array, Object], prop: String, span: { type: [Number, String], diff --git a/packages/mixins/origin.js b/packages/mixins/origin.js new file mode 100644 index 0000000..eb79145 --- /dev/null +++ b/packages/mixins/origin.js @@ -0,0 +1,59 @@ +export default { + data() { + return { + originData: {}, + originProps: {}, + }; + }, + created() { + const { originData, originProps, ...other } = this._data; + this.originData = this.cloneDeep(other); + this.originProps = this.cloneDeep(this._props); + }, + methods: { + // 深克隆对象 + cloneDeep(obj) { + if (typeof obj !== 'object') { + return obj; + } + if (!obj) { + return obj; + } + if (obj instanceof Date) { + return new Date(obj); + } + if (obj instanceof RegExp) { + return new RegExp(obj); + } + if (obj instanceof Function) { + return obj; + } + let newObj; + if (obj instanceof Array) { + newObj = []; + for (let i = 0, len = obj.length; i < len; i++) { + newObj.push(this.cloneDeep(obj[i])); + } + return newObj; + } + newObj = {}; + for (let key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (typeof obj[key] !== 'object') { + newObj[key] = obj[key]; + } else { + newObj[key] = this.cloneDeep(obj[key]); + } + } + } + return newObj; + }, + // 获取初始值 + getOriginData(key) { + if (key) { + return this.cloneDeep(this.originData)[key]; + } + return this.cloneDeep(this.originData); + }, + }, +}; diff --git a/packages/schema-form/index.vue b/packages/schema-form/index.vue index 7bcd483..cfad110 100644 --- a/packages/schema-form/index.vue +++ b/packages/schema-form/index.vue @@ -34,7 +34,8 @@ export default { const item = props.item || {}; let content = []; if (item.render && typeof item.render === 'function') { - content = context.props.render; + console.log('render'); + content = item.render(props.value, h); } if (item.children) { if (Array.isArray(item.children)) { diff --git a/packages/schema-page/index copy.scss b/packages/schema-page/index copy.scss new file mode 100644 index 0000000..dc37af8 --- /dev/null +++ b/packages/schema-page/index copy.scss @@ -0,0 +1,72 @@ +.z-schema { + &__header { + margin-bottom: 10px; + } + &__filter { + border: 1px solid #ebeef5; + padding-top: 10px; + border-radius: 4px; + margin-bottom: 10px; + } + &__action { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + line-height: 1; + .el-button + .el-button { + margin-left: 0; + } + .el-button { + margin-right: 10px; + margin-bottom: 10px; + } + } + &__table { + &-operation { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + .el-button + .el-button { + margin-left: 0; + } + .el-button { + margin-right: 10px; + padding-top: 6px; + padding-bottom: 6px; + } + } + } + &__dialog-button { + display: flex; + align-items: center; + justify-content: center; + padding-top: 10px; + } + &__footer { + margin-top: 10px; + text-align: right; + display: flex; + justify-content: space-between; + align-items: center; + .selection-info { + word-break: break-all; + white-space: nowrap; + font-size: 12px; + color: #606266; + .num { + color: #000; + font-weight: bold; + padding: 0 5px; + font-size: 16px; + } + .el-button { + margin-left: 5px; + } + } + .el-pagination { + flex: auto; + } + } +} \ No newline at end of file diff --git a/packages/schema-page/index copy.vue b/packages/schema-page/index copy.vue new file mode 100644 index 0000000..5d54a1d --- /dev/null +++ b/packages/schema-page/index copy.vue @@ -0,0 +1,649 @@ + + + + + diff --git a/packages/schema-page/index.scss b/packages/schema-page/index.scss new file mode 100644 index 0000000..a5a1f68 --- /dev/null +++ b/packages/schema-page/index.scss @@ -0,0 +1,105 @@ +.z-schema-page { + &__header { + margin-bottom: 10px; + } + &__filter { + border: 1px solid #ebeef5; + padding-top: 10px; + padding-right: 10px; + border-radius: 4px; + margin-bottom: 10px; + } + &__action { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + line-height: 1; + .el-button + .el-button { + margin-left: 0; + } + .el-button { + margin-right: 10px; + margin-bottom: 10px; + } + } + &__table { + &-operation { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + .el-button + .el-button { + margin-left: 0; + } + .el-button { + margin-right: 8px; + padding-top: 6px; + padding-bottom: 6px; + } + } + } + &__dialog-button { + display: flex; + align-items: center; + justify-content: center; + padding-top: 10px; + } + &__footer { + margin-top: 10px; + text-align: right; + display: flex; + justify-content: space-between; + align-items: center; + .selection-info { + word-break: break-all; + white-space: nowrap; + font-size: 12px; + color: #606266; + .num { + color: #333; + font-weight: bold; + padding: 0 5px; + font-size: 16px; + } + .el-button { + margin-left: 5px; + } + } + .el-pagination { + flex: auto; + } + } +} + +.z-loading-toast { + $toast-color: #fff; + $toast-bg-color: #000; + background-color: rgba($toast-bg-color, 0); + .el-loading-spinner { + width: 200px; + height: 120px; + margin: auto; + background-color: rgba($toast-bg-color, 0.7); + color: $toast-color; + left: 50%; + transform: translateX(-50%); + border-radius: 16px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .path { + stroke: $toast-color; + } + .el-icon-loading { + font-size: 32px; + color: $toast-color; + } + .el-loading-text { + font-size: 14px; + color: $toast-color; + margin-top: 10px; + } + } +} \ No newline at end of file diff --git a/packages/schema-page/index.vue b/packages/schema-page/index.vue new file mode 100644 index 0000000..c2b7377 --- /dev/null +++ b/packages/schema-page/index.vue @@ -0,0 +1,436 @@ + + + + + diff --git a/packages/schema-table/index.vue b/packages/schema-table/index.vue index 1bc429c..06c9c06 100644 --- a/packages/schema-table/index.vue +++ b/packages/schema-table/index.vue @@ -32,7 +32,7 @@ export default { render(h) { const schema = this.schema || {}; const _props = schema.props || {}; - const _on = schema.on || {}; + const _on = schema.on || this.$listeners || {}; return h('z-table', { props: { value: this.model, columns: schema.items, ..._props }, on: _on, scopedSlots: this.$scopedSlots }); }, }; diff --git a/packages/schema/index.scss b/packages/schema/index.scss deleted file mode 100644 index dc37af8..0000000 --- a/packages/schema/index.scss +++ /dev/null @@ -1,72 +0,0 @@ -.z-schema { - &__header { - margin-bottom: 10px; - } - &__filter { - border: 1px solid #ebeef5; - padding-top: 10px; - border-radius: 4px; - margin-bottom: 10px; - } - &__action { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-start; - line-height: 1; - .el-button + .el-button { - margin-left: 0; - } - .el-button { - margin-right: 10px; - margin-bottom: 10px; - } - } - &__table { - &-operation { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-start; - .el-button + .el-button { - margin-left: 0; - } - .el-button { - margin-right: 10px; - padding-top: 6px; - padding-bottom: 6px; - } - } - } - &__dialog-button { - display: flex; - align-items: center; - justify-content: center; - padding-top: 10px; - } - &__footer { - margin-top: 10px; - text-align: right; - display: flex; - justify-content: space-between; - align-items: center; - .selection-info { - word-break: break-all; - white-space: nowrap; - font-size: 12px; - color: #606266; - .num { - color: #000; - font-weight: bold; - padding: 0 5px; - font-size: 16px; - } - .el-button { - margin-left: 5px; - } - } - .el-pagination { - flex: auto; - } - } -} \ No newline at end of file diff --git a/packages/schema/index.vue b/packages/schema/index.vue deleted file mode 100644 index a74d1fd..0000000 --- a/packages/schema/index.vue +++ /dev/null @@ -1,649 +0,0 @@ - - - - - diff --git a/packages/table/editable.vue b/packages/table/editable.vue index 431ff71..44299e7 100644 --- a/packages/table/editable.vue +++ b/packages/table/editable.vue @@ -27,7 +27,15 @@