├── .eslintignore
├── renovate.json
├── .npmignore
├── jsconfig.json
├── src
├── main.js
├── utils
│ └── type.js
└── components
│ └── Table
│ ├── ComponentsMapping.vue
│ ├── ElementsMapping.vue
│ └── index.vue
├── example
├── src
│ ├── views
│ │ └── ExampleTable.vue
│ ├── components
│ │ ├── data.js
│ │ ├── Layout.vue
│ │ ├── table-children-b.vue
│ │ ├── table-children-a.vue
│ │ ├── banner.vue
│ │ └── table-list.vue
│ ├── styles
│ │ └── variables.scss
│ └── directive
│ │ └── clipboard.js
├── router.js
├── main.js
├── App.vue
└── index.html
├── .gitignore
├── LICENSE
├── babel.config.js
├── .eslintrc.js
├── README.md
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public/
3 | build/
4 | config/
5 | dist/
6 | lib/
7 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "pin": false
6 | }
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
2 | CVS
3 | .svn
4 | .hg
5 | .lock-wscript
6 | .wafpickle-N
7 | .*.swp
8 | .DS_Store
9 | ._*
10 | yarn.lock
11 | npm-debug.log
12 | .npmrc
13 | node_modules
14 | config.gypi
15 | *.orig
16 | package-lock.json
17 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "module": "commonjs",
5 | "paths": {
6 | "root/*": ["./*"],
7 | "@/*": ["./src/*"],
8 | "example/*": ["./example/src/*"]
9 | }
10 | },
11 | "exclude": ["node_modules"]
12 | }
13 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import HocElTable from '@/components/Table'
2 |
3 | const install = function (app) {
4 | app.component('HocElTable', HocElTable)
5 | }
6 |
7 | if (typeof window !== 'undefined' && window.Vue) {
8 | install(window.Vue)
9 | }
10 |
11 | export default {
12 | version: '2.2.0',
13 | install,
14 | HocElTable
15 | }
16 |
--------------------------------------------------------------------------------
/example/src/views/ExampleTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
--------------------------------------------------------------------------------
/example/router.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router'
2 |
3 | const importModule = (filePath) => {
4 | return () => import(`example/${filePath}`)
5 | }
6 |
7 | const routes = [{
8 | path: '/',
9 | component: importModule('views/ExampleTable')
10 | }]
11 |
12 | export default createRouter({
13 | routes,
14 | history: createWebHistory()
15 | })
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /dist
13 | /dist-ssr
14 | /lib
15 |
16 | # misc
17 | .DS_Store
18 | *.local
19 | .vscode/
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | .eslintcache
25 |
--------------------------------------------------------------------------------
/example/src/components/data.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data: [
3 | { id: 0, name: '王小虎1', isForbid: false },
4 | { id: 1, name: '王小虎2', isForbid: false },
5 | { id: 2, name: '王小虎3', isForbid: false }
6 | ],
7 | meta: {
8 | pagination: {
9 | total: 3,
10 | count: 10,
11 | perPage: 10,
12 | currentPage: 1,
13 | totalPages: 1
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import router from './router.js'
3 | import ElementPlus from 'element-plus'
4 |
5 | // import HocElTable from 'root/lib/hoc-el-table' // Switch to bundle lib
6 | import HocElTable from '@/main.js'
7 | import App from './App.vue'
8 |
9 | import 'example/styles/variables.scss'
10 |
11 | createApp(App)
12 | .use(router)
13 | .use(ElementPlus)
14 | .use(HocElTable)
15 | .mount('#app')
16 |
--------------------------------------------------------------------------------
/example/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
26 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/src/components/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
31 |
--------------------------------------------------------------------------------
/example/src/components/table-children-b.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
{{ modelValue }}
9 |
10 |
11 |
12 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/src/utils/type.js:
--------------------------------------------------------------------------------
1 | const toString = Object.prototype.toString
2 |
3 | export function isFunction (obj) {
4 | return typeof (obj) === 'function'
5 | }
6 |
7 | export function isObject (obj) {
8 | return obj === Object(obj)
9 | }
10 |
11 | export function isArray (obj) {
12 | return toString.call(obj) === '[object Array]'
13 | }
14 |
15 | export function isDate (obj) {
16 | return toString.call(obj) === '[object Date]'
17 | }
18 |
19 | export function isRegExp (obj) {
20 | return toString.call(obj) === '[object RegExp]'
21 | }
22 | export function isBoolean (obj) {
23 | return toString.call(obj) === '[object Boolean]'
24 | }
25 |
26 | export function isNumberical (obj) {
27 | return (typeof (obj) === 'number' || typeof (obj) === 'string') && !isNaN(obj - parseFloat(obj))
28 | }
29 |
--------------------------------------------------------------------------------
/example/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 |
2 | /* theme color */
3 | $--color-primary: #b38358;
4 | $--color-success: #52C41A;
5 | $--color-warning: #FAAD14;
6 | $--color-danger: #FA5555;
7 | $--color-info: #909399;
8 |
9 |
10 | // 按需导入,重写覆盖自己所需样式
11 | @forward "element-plus/theme-chalk/src/common/var.scss" with (
12 | $colors: (
13 | "primary": (
14 | "base": $--color-primary,
15 | ),
16 | "success": (
17 | "base": $--color-success,
18 | ),
19 | "warning": (
20 | "base": $--color-warning,
21 | ),
22 | "danger": (
23 | "base": $--color-danger,
24 | ),
25 | "info": (
26 | "base": $--color-info,
27 | ),
28 |
29 | ),
30 | $border-radius: (
31 | 'base': 4px,
32 | 'small': 2px,
33 | 'round': 20px,
34 | 'circle': 100%,
35 | ),
36 | );
37 |
38 | @import 'element-plus/theme-chalk/src/index.scss';
39 |
--------------------------------------------------------------------------------
/src/components/Table/ComponentsMapping.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-present, Wisdom
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/src/components/table-children-a.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 轮播组件
5 |
6 |
10 |
11 | {{ modelValue }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
32 |
52 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const babelConfig = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | chrome: '58',
8 | ie: '11'
9 | }
10 | }
11 | ]
12 | ],
13 | plugins: [
14 | [
15 | '@babel/plugin-transform-runtime',
16 | {
17 | corejs: false,
18 | helpers: true,
19 | regenerator: true,
20 | useESModules: false
21 | }
22 | ],
23 | '@babel/plugin-syntax-dynamic-import',
24 | '@babel/plugin-syntax-import-meta',
25 | ['@babel/plugin-proposal-class-properties', { loose: false }],
26 | ['@babel/plugin-proposal-private-methods', { loose: false }],
27 | '@babel/plugin-proposal-json-strings',
28 | [
29 | '@babel/plugin-proposal-decorators',
30 | {
31 | legacy: true
32 | }
33 | ],
34 | '@babel/plugin-proposal-function-sent',
35 | '@babel/plugin-proposal-export-default-from',
36 | '@babel/plugin-proposal-export-namespace-from',
37 | '@babel/plugin-proposal-numeric-separator',
38 | '@babel/plugin-proposal-throw-expressions',
39 | '@babel/plugin-transform-modules-commonjs'
40 | ]
41 | }
42 |
43 | module.exports = babelConfig
44 |
--------------------------------------------------------------------------------
/example/src/directive/clipboard.js:
--------------------------------------------------------------------------------
1 | import Clipboard from 'clipboard'
2 |
3 | const clipboard = {
4 | beforeMount: function (el, binding, vnode) {
5 | if (binding.arg === 'success') {
6 | el._v_clipboard_success = binding.value
7 | } else if (binding.arg === 'error') {
8 | el._v_clipboard_error = binding.value
9 | } else {
10 | const clipboard = new Clipboard(el, {
11 | text: function () { return binding.value },
12 | action: function () { return binding.arg === 'cut' ? 'cut' : 'copy' }
13 | })
14 | clipboard.on('success', function (e) {
15 | const callback = el._v_clipboard_success
16 | callback && callback(e)
17 | })
18 | clipboard.on('error', function (e) {
19 | const callback = el._v_clipboard_error
20 | callback && callback(e)
21 | })
22 | el._v_clipboard = clipboard
23 | }
24 | },
25 | updated: function (el, binding) {
26 | if (binding.arg === 'success') {
27 | el._v_clipboard_success = binding.value
28 | } else if (binding.arg === 'error') {
29 | el._v_clipboard_error = binding.value
30 | } else {
31 | el._v_clipboard.text = function () { return binding.value }
32 | el._v_clipboard.action = function () { return binding.arg === 'cut' ? 'cut' : 'copy' }
33 | }
34 | },
35 | unmounted: function (el, binding) {
36 | if (binding.arg === 'success') {
37 | delete el._v_clipboard_success
38 | } else if (binding.arg === 'error') {
39 | delete el._v_clipboard_error
40 | } else {
41 | el._v_clipboard.destroy()
42 | delete el._v_clipboard
43 | }
44 | }
45 | }
46 |
47 | export {
48 | clipboard
49 | }
50 |
--------------------------------------------------------------------------------
/example/src/components/banner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
📦 @hoc-element/table
18 |
19 |
20 |
21 |
22 |
32 |
33 |
59 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | es6: true,
6 | browser: true,
7 | jest: true
8 | },
9 | globals: {
10 | defineProps: 'readonly',
11 | defineEmits: 'readonly',
12 | defineExpose: 'readonly',
13 | withDefaults: 'readonly'
14 | },
15 | extends: [
16 | 'eslint:recommended',
17 | 'plugin:vue/base',
18 | 'plugin:vue/vue3-essential',
19 | 'plugin:vue/vue3-strongly-recommended',
20 | 'plugin:vue/vue3-recommended'
21 | ],
22 | parserOptions: {
23 | ecmaFeatures: {
24 | legacyDecorators: true
25 | },
26 | parser: '@babel/eslint-parser'
27 | },
28 | rules: {
29 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
30 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
31 |
32 | // vue
33 | 'vue/multi-word-component-names': 0,
34 | 'vue/no-unused-components': 1,
35 | 'vue/no-mutating-props': 0,
36 | 'vue/script-setup-uses-vars': 'error',
37 | 'vue/html-self-closing': ['error', {
38 | html: {
39 | void: 'never',
40 | normal: 'never',
41 | component: 'always'
42 | },
43 | svg: 'always',
44 | math: 'always'
45 | }],
46 |
47 | // js
48 | 'no-unused-vars': 1,
49 | 'no-undef': 1,
50 | // https://eslint.org/docs/rules/no-var
51 | 'no-var': 'error',
52 | // https://cn.eslint.org/docs/rules/no-trailing-spaces
53 | 'no-trailing-spaces': 2, // 禁用行尾空白
54 | 'comma-style': ['error', 'last'],
55 | 'comma-dangle': ['error', 'never'],
56 | 'no-irregular-whitespace': 2,
57 | 'no-multi-spaces': 1,
58 | 'no-multiple-empty-lines': [
59 | 2,
60 | {
61 | max: 1
62 | }
63 | ],
64 | // https://cn.eslint.org/docs/rules/eol-last
65 | 'eol-last': 2,
66 | quotes: [
67 | 'error',
68 | 'single',
69 | {
70 | avoidEscape: true,
71 | allowTemplateLiterals: true
72 | }
73 | ],
74 | // https://eslint.org/docs/rules/prefer-const
75 | 'prefer-const': 2,
76 | camelcase: ['error', { properties: 'never' }],
77 | indent: ['error', 2, { SwitchCase: 1 }],
78 | semi: ['error', 'never'],
79 | 'space-before-function-paren': 'error'
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @hoc-element/table
2 |
3 | [](https://www.npmjs.com/package/@hoc-element/table)
4 | [](https://github.com/pdsuwwz/hoc-element-table/blob/main/LICENSE)
5 |
6 | 📦 基于 Webpack 5 构建,重构成 Vue 3.x,二次封装了 element-plus 库中的 table,通过配置文件的方式即可生成 `table` 表格, 无需再写大量的诸如 `` 的模板,实现更高的自由度,旨在减少重复的操作,让开发变得更高效。
7 |
8 | > 该组件库可供学习、参考和用于二次开发
9 |
10 | **[Live demo](https://pdsuwwz.github.io/hoc-element-table)**
11 |
12 | ## Version
13 |
14 | * Vue 3.x 版本 | [Vue 2.x 版本](https://github.com/pdsuwwz/hoc-element-table/tree/vue2.0)
15 |
16 | ## Screenshot
17 |
18 |
19 |
20 | ## Environment Support
21 |
22 | * Vue 3.2.x
23 |
24 | ## Install
25 |
26 | ```shell
27 | npm install @hoc-element/table
28 |
29 | # or
30 |
31 | pnpm add @hoc-element/table
32 | ```
33 |
34 | ## Quick Start
35 |
36 | * 使用前请安装 [element-plus](https://www.npmjs.com/package/element-plus)
37 |
38 | ```js
39 | import { createApp } from 'vue'
40 | import ElementPlus from 'element-plus'
41 | import HocElTable from '@hoc-element/table'
42 | import App from './App.vue'
43 |
44 | createApp(App)
45 | .use(ElementPlus)
46 | .use(HocElTable)
47 | .mount('#app')
48 |
49 | ```
50 |
51 | ## Changelog
52 |
53 | Detailed changes for each release are documented in the [release notes](https://github.com/pdsuwwz/hoc-element-table/releases)
54 |
55 | ## Feature
56 |
57 | - [x] 基于 Webpack 5 构建
58 | - [x] 全面支持 Vue 3
59 | - [x] 支持 `JSON` 序列化表格快速配置
60 | - [x] 已内置 `Pagination` 分页
61 | - [x] 支持自定义 `prop` 列名
62 | - [x] 支持单元格内容自定义渲染 【见 [单元格渲染配置说明](#单元格渲染配置说明)】
63 | - [x] 支持自定义单元格 `style` 样式
64 | - [x] 支持绑定自定义指令 `directives`
65 | - [x] 支持绑定 `element-plus` 原生 `Table` 的 `Events` 和 `Methods`
66 | - [x] 支持 `Header` 和 `Pagination` 的显隐控制
67 |
68 | ## 单元格渲染配置说明
69 |
70 | | 方法 | 说明 | 场景 |
71 | | -------- | -------- | -------- |
72 | | --- | 渲染单元格的 attrs.prop 对应的键值 | 适用于直接显示 prop 的值场景 |
73 | | render | 渲染字符串 | 适用于对默认 prop 的值做一些微处理的场景 |
74 | | renderHTML | 渲染指定的 DOM 元素 | 适用于展示 Action,一般用在最后一列(目前只支持 el-button 的渲染,详见 Example) |
75 | | renderComponent | 渲染组件 | 适用于单元格内需要展示复杂内容的场景,详见 Example |
76 |
77 | ## Example
78 |
79 | 这是比较全的例子,几乎囊括了 API 的所有用法,源码戳这: [Code](https://github.com/pdsuwwz/hoc-element-table/tree/main/example/src/components/table-list.vue)
80 |
81 | ## Deploy
82 |
83 | 见仓库 [📥 hoc-element-table-build](https://github.com/pdsuwwz/hoc-element-table-build)
84 |
--------------------------------------------------------------------------------
/src/components/Table/ElementsMapping.vue:
--------------------------------------------------------------------------------
1 |
2 |
116 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@hoc-element/table",
3 | "version": "2.2.0",
4 | "main": "lib/hoc-el-table.js",
5 | "description": "📦 A Vue3.x Table Component built on Webpack 5 that follows the Configuration",
6 | "author": "Wisdom ",
7 | "license": "MIT",
8 | "scripts": {
9 | "build": "cross-env NODE_ENV=production webpack --progress --config build/webpack.config.js",
10 | "dev:example": "cross-env NODE_ENV=development webpack serve --progress --config build/webpack.example.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/pdsuwwz/hoc-element-table.git"
15 | },
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "peerDependencies": {
20 | "element-plus": ">=2.0.0",
21 | "vue": ">=3.2.0"
22 | },
23 | "engines": {
24 | "node": ">=14.19.0"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.17.12",
28 | "@babel/eslint-parser": "^7.17.0",
29 | "@babel/eslint-plugin": "^7.17.7",
30 | "@babel/plugin-proposal-class-properties": "^7.17.12",
31 | "@babel/plugin-proposal-decorators": "^7.17.12",
32 | "@babel/plugin-proposal-export-default-from": "^7.17.12",
33 | "@babel/plugin-proposal-export-namespace-from": "^7.17.12",
34 | "@babel/plugin-proposal-function-sent": "^7.17.12",
35 | "@babel/plugin-proposal-json-strings": "^7.17.12",
36 | "@babel/plugin-proposal-numeric-separator": "^7.16.7",
37 | "@babel/plugin-proposal-private-methods": "^7.17.12",
38 | "@babel/plugin-proposal-throw-expressions": "^7.16.7",
39 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
40 | "@babel/plugin-syntax-import-meta": "^7.10.4",
41 | "@babel/plugin-transform-modules-commonjs": "^7.17.12",
42 | "@babel/plugin-transform-runtime": "^7.17.12",
43 | "@babel/preset-env": "^7.17.12",
44 | "@babel/runtime": "^7.17.9",
45 | "@babel/runtime-corejs2": "^7.17.11",
46 | "@types/lodash-es": "^4.17.6",
47 | "@vue/compiler-sfc": "^3.2.33",
48 | "babel-loader": "^9.0.0",
49 | "clipboard": "^2.0.11",
50 | "core-js": "^3.22.5",
51 | "cross-env": "^7.0.3",
52 | "css-loader": "^6.7.1",
53 | "element-plus": "^2.2.0",
54 | "eslint": "^8.0.0",
55 | "eslint-friendly-formatter": "^4.0.1",
56 | "eslint-plugin-import": "^2.26.0",
57 | "eslint-plugin-jsx-a11y": "^6.5.1",
58 | "eslint-plugin-node": "^11.1.0",
59 | "eslint-plugin-promise": "^6.0.0",
60 | "eslint-plugin-vue": "9.9.0",
61 | "eslint-webpack-plugin": "^4.0.0",
62 | "file-loader": "^6.2.0",
63 | "html-webpack-plugin": "^5.5.0",
64 | "sass": "^1.51.0",
65 | "sass-loader": "13.2.0",
66 | "style-loader": "^3.3.1",
67 | "url-loader": "^4.1.1",
68 | "vue": "^3.2.33",
69 | "vue-loader": "^17.0.0",
70 | "vue-router": "^4.0.15",
71 | "vue-style-loader": "^4.1.3",
72 | "webpack": "^5.72.1",
73 | "webpack-cli": "^5.0.0",
74 | "webpack-dev-server": "^4.9.0"
75 | },
76 | "browserslist": [
77 | "> 1%",
78 | "last 2 versions",
79 | "not ie <= 8"
80 | ],
81 | "keywords": [
82 | "vue",
83 | "element-plus",
84 | "table",
85 | "hoc",
86 | "高阶组件",
87 | "组件化",
88 | "表格"
89 | ]
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/Table/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
42 |
43 |
44 | {
50 | handleNativeClick(getAttrsValue(item), $event)
51 | }"
52 | />
53 |
54 |
57 | {{ getValue(scope, item) }}
58 |
59 |
60 |
61 |
62 |
63 |
79 |
80 |
81 |
82 |
83 |
298 |
299 |
331 |
--------------------------------------------------------------------------------
/example/src/components/table-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
364 |
365 |
374 |
--------------------------------------------------------------------------------