├── static
└── .gitkeep
├── src
├── components
│ ├── dragItem
│ │ ├── detailSublist.vue
│ │ ├── segmentingLine.vue
│ │ ├── threeColumn.vue
│ │ ├── z_readme.text
│ │ ├── showDetails.vue
│ │ ├── twoColumns.vue
│ │ ├── tableLayout.vue
│ │ ├── single.备份.vue
│ │ ├── children.vue
│ │ └── single.vue
│ ├── dragList
│ │ ├── z_readme.text
│ │ └── dragCompent.vue
│ ├── vue-slicksort
│ │ ├── HandleDirective.js
│ │ ├── index.js
│ │ ├── components.js
│ │ ├── Manager.js
│ │ ├── ElementMixin.js
│ │ ├── utils.js
│ │ └── ContainerMixin.js
│ └── HelloWorld.vue
├── assets
│ ├── logo.png
│ └── show.gif
├── store
│ ├── index.js
│ └── modules
│ │ ├── base.js
│ │ ├── carPicker.js
│ │ └── dragItemDate.js
├── main.js
├── view
│ ├── testDemo.vue
│ ├── vueSlicksortPractice.vue
│ ├── canBack.vue
│ └── dragDemo.vue
├── router
│ └── index.js
└── App.vue
├── config
├── prod.env.js
├── dev.env.js
└── index.js
├── .editorconfig
├── .gitignore
├── .babelrc
├── .postcssrc.js
├── index.html
├── README.md
└── package.json
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/dragItem/detailSublist.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/dragItem/segmentingLine.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/dragItem/threeColumn.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/dragList/z_readme.text:
--------------------------------------------------------------------------------
1 | 这是设计器左边的 组件列表
--------------------------------------------------------------------------------
/src/components/dragItem/z_readme.text:
--------------------------------------------------------------------------------
1 | 这是设计器 中间的拖动组件
2 | 分为5种:
3 | 一行两列 一行三列 表格 明细子表 分割线。
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YalongYan/vue-drag-layout/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/show.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YalongYan/vue-drag-layout/HEAD/src/assets/show.gif
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/HandleDirective.js:
--------------------------------------------------------------------------------
1 | // Export Sortable Element Handle Directive
2 | export const HandleDirective = {
3 | bind(el) {
4 | el.sortableHandle = true;
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/index.js:
--------------------------------------------------------------------------------
1 | export { ElementMixin } from './ElementMixin';
2 | export { ContainerMixin } from './ContainerMixin';
3 | export { HandleDirective } from './HandleDirective';
4 | export { SlickList, SlickItem } from './components';
5 |
6 | export { arrayMove } from './utils';
7 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-drag2
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 |
4 | // import base from './modules/base';
5 | // import user from './modules/dragItemDate';
6 | // import carPicker from './modules/carPicker';
7 | import dragItemDate from './modules/dragItemDate';
8 |
9 | Vue.use(Vuex);
10 |
11 | export default new Vuex.Store({
12 | modules: {
13 | dragItemDate
14 | // base,
15 | // user,
16 | // carPicker,
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/components.js:
--------------------------------------------------------------------------------
1 | import { ElementMixin } from './ElementMixin';
2 | import { ContainerMixin } from './ContainerMixin';
3 |
4 | export const SlickList = {
5 | name: 'slick-list',
6 | mixins: [ ContainerMixin ],
7 | render (h) {
8 | return h('div', this.$slots.default);
9 | },
10 | };
11 |
12 | export const SlickItem = {
13 | name: 'slick-item',
14 | mixins: [ ElementMixin ],
15 | render (h) {
16 | return h('div', this.$slots.default);
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import 'babel-polyfill'
4 | import Vue from 'vue'
5 | // import App from './App'
6 | import store from './store'
7 | import router from '@/router';
8 | Vue.config.productionTip = false
9 |
10 | /* eslint-disable no-new */
11 | new Vue({
12 | el: '#app',
13 | router,
14 | store
15 | // components: { App },
16 | // template: ''
17 | })
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-slicksort-practice
2 | ### 目前的效果如下
3 | 
4 |
5 | > A Vue.js project
6 |
7 | ## Build Setup
8 |
9 | ``` bash
10 | # install dependencies
11 | npm install
12 |
13 | # serve with hot reload at localhost:8080
14 | npm run dev
15 | 访问网址: http://localhost:8080/#/dragDemo
16 |
17 | # build for production with minification
18 | npm run build
19 |
20 | # build for production and view the bundle analyzer report
21 | npm run build --report
22 | ```
23 |
24 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
25 |
--------------------------------------------------------------------------------
/src/view/testDemo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 这是testDemo页面
4 |
5 |
6 |
7 |
38 |
41 |
--------------------------------------------------------------------------------
/src/view/vueSlicksortPractice.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
37 |
40 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/Manager.js:
--------------------------------------------------------------------------------
1 | export default class Manager {
2 | constructor() {
3 | this.refs = {};
4 | }
5 |
6 | add(collection, ref) {
7 | if (!this.refs[collection]) {
8 | this.refs[collection] = [];
9 | }
10 |
11 | this.refs[collection].push(ref);
12 | }
13 |
14 | remove(collection, ref) {
15 | const index = this.getIndex(collection, ref);
16 |
17 | if (index !== -1) {
18 | this.refs[collection].splice(index, 1);
19 | }
20 | }
21 |
22 | isActive() {
23 | return this.active;
24 | }
25 |
26 | getActive() {
27 | return this.refs[this.active.collection].find(({node}) => node.sortableInfo.index == this.active.index);
28 | }
29 |
30 | getIndex(collection, ref) {
31 | return this.refs[collection].indexOf(ref);
32 | }
33 |
34 | getOrderedRefs(collection = this.active.collection) {
35 | return this.refs[collection].sort((a, b) => {
36 | return a.node.sortableInfo.index - b.node.sortableInfo.index;
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import App from '@/App'
4 | import dragDemo from '@/view/dragDemo'
5 | import vueSlicksortPractice from '@/view/vueSlicksortPractice'
6 | import testDemo from '@/view/testDemo'
7 | import canBack from '@/view/canBack'
8 |
9 | Vue.use(Router)
10 |
11 | export default new Router({
12 | routes: [
13 | // {
14 | // path: '/',
15 | // redirect: {
16 | // path: '/index'
17 | // }
18 | // },
19 | {
20 | path: '/',
21 | component: App
22 | },
23 | {
24 | path: '/dragDemo',
25 | name: 'dragDemo',
26 | component: dragDemo
27 | },
28 | {
29 | path: '/canBack',
30 | name: 'canBack',
31 | component: canBack
32 | },
33 | {
34 | path: '/vueSlicksortPractice',
35 | name: 'vueSlicksortPractice',
36 | component: vueSlicksortPractice
37 | },
38 | {
39 | path: '/testDemo',
40 | name: 'testDemo',
41 | component: testDemo
42 | }
43 | ]
44 | })
45 |
--------------------------------------------------------------------------------
/src/view/canBack.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 这是canBack页面
4 |
点我跳转
5 |
点我返回
6 |
7 |
8 |
9 |
48 |
51 |
--------------------------------------------------------------------------------
/src/store/modules/base.js:
--------------------------------------------------------------------------------
1 | import types from '../mutation-types';
2 |
3 | const state = {
4 | isShowHeader: false,
5 | currentPageTitle: '',
6 | isReqError: false,
7 | fetchLoading: false,
8 | };
9 |
10 | const actions = {
11 | setShowHeader({ commit }, status) {
12 | commit(types.SET_SHOW_HEADER, status);
13 | },
14 | setCurrentPageTitle({ commit }, title) {
15 | commit(types.SET_CURRENT_PAGE_TITLE, title);
16 | },
17 | setNetworkError({ commit }, status) {
18 | commit(types.SET_NETWORK_ERROR, status);
19 | },
20 | setFetchLoading({ commit }, status) {
21 | commit(types.FETCH_LOADING, status);
22 | },
23 | };
24 |
25 | const getters = {
26 | isShowHeader: state => state.isShowHeader,
27 | fetchLoading: state => state.fetchLoading,
28 | currentPageTitle: state => state.currentPageTitle,
29 | isReqError: state => state.isReqError,
30 | };
31 |
32 | /**
33 | * 提交同步请求
34 | * */
35 | const mutations = {
36 | [types.SET_SHOW_HEADER](state, isShowHeader) {
37 | state.isShowHeader = isShowHeader;
38 | },
39 | [types.FETCH_LOADING](state, fetchLoading) {
40 | state.fetchLoading = fetchLoading;
41 | },
42 | [types.SET_CURRENT_PAGE_TITLE](state, title) {
43 | state.currentPageTitle = title;
44 | },
45 | [types.SET_NETWORK_ERROR](state, isReqError) {
46 | state.isReqError = isReqError;
47 | },
48 | };
49 |
50 | export default {
51 | state,
52 | actions,
53 | getters,
54 | mutations,
55 | };
56 |
--------------------------------------------------------------------------------
/src/store/modules/carPicker.js:
--------------------------------------------------------------------------------
1 | /*
2 | * 车辆品牌选择
3 | */
4 | import types from '../mutation-types';
5 |
6 | const state = {
7 | carData: {
8 | brand: {
9 | name: '不限品牌',
10 | value: '',
11 | },
12 | series: {
13 | name: '不限车系',
14 | value: '',
15 | },
16 | type: {
17 | name: '不限车型',
18 | value: '',
19 | },
20 | },
21 | };
22 |
23 | const getters = {
24 | getCarBrand: state => state.carData.brand,
25 | getCarSeries: state => state.carData.series,
26 | getCarType: state => state.carData.type,
27 | };
28 |
29 | const mutations = {
30 | [types.SET_CAR_BRAND](state, data) {
31 | state.carData.brand = data;
32 | state.carData.series = {
33 | name: '不限车系',
34 | value: '',
35 | };
36 | state.carData.type = {
37 | name: '不限车型',
38 | value: '',
39 | };
40 | },
41 | [types.SET_CAR_SERIES](state, data) {
42 | state.carData.series = data;
43 | state.carData.type = {
44 | name: '不限车型',
45 | value: '',
46 | };
47 | },
48 | [types.SET_CAR_TYPE](state, data) {
49 | state.carData.type = data;
50 | },
51 | };
52 |
53 | const actions = {
54 | setCarBrand({ commit }, data) {
55 | commit(types.SET_CAR_BRAND, data);
56 | },
57 | setCarSeries({ commit }, data) {
58 | commit(types.SET_CAR_SERIES, data);
59 | },
60 | setCarType({ commit }, data) {
61 | commit(types.SET_CAR_TYPE, data);
62 | },
63 | };
64 |
65 | export default {
66 | state,
67 | getters,
68 | mutations,
69 | actions,
70 | };
71 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/ElementMixin.js:
--------------------------------------------------------------------------------
1 | // Export Sortable Element Component Mixin
2 | export const ElementMixin = {
3 | inject: ['manager'],
4 | props: {
5 | index: {
6 | type: Number,
7 | required: true,
8 | },
9 | collection: {
10 | type: [String, Number],
11 | default: 'default',
12 | },
13 | disabled: {
14 | type: Boolean,
15 | default: false,
16 | },
17 | },
18 |
19 | mounted() {
20 | const {collection, disabled, index} = this.$props;
21 |
22 | if (!disabled) {
23 | this.setDraggable(collection, index);
24 | }
25 | },
26 |
27 | watch: {
28 | index(newIndex) {
29 | if (this.$el && this.$el.sortableInfo) {
30 | this.$el.sortableInfo.index = newIndex;
31 | }
32 | },
33 | disabled(isDisabled) {
34 | if (isDisabled) {
35 | this.removeDraggable(this.collection);
36 | } else {
37 | this.setDraggable(this.collection, this.index);
38 | }
39 | },
40 | collection(newCollection, oldCollection) {
41 | this.removeDraggable(oldCollection);
42 | this.setDraggable(newCollection, this.index);
43 | },
44 | },
45 |
46 | beforeDestroy() {
47 | const {collection, disabled} = this;
48 |
49 | if (!disabled) this.removeDraggable(collection);
50 | },
51 | methods: {
52 | setDraggable(collection, index) {
53 | const node = this.$el;
54 |
55 | node.sortableInfo = {
56 | index,
57 | collection,
58 | manager: this.manager,
59 | };
60 |
61 | this.ref = {node};
62 | this.manager.add(collection, this.ref);
63 | },
64 |
65 | removeDraggable(collection) {
66 | this.manager.remove(collection, this.ref);
67 | },
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/utils.js:
--------------------------------------------------------------------------------
1 | export function arrayMove(arr, previousIndex, newIndex) {
2 | const array = arr.slice(0);
3 | if (newIndex >= array.length) {
4 | let k = newIndex - array.length;
5 | while (k-- + 1) {
6 | array.push(undefined);
7 | }
8 | }
9 | array.splice(newIndex, 0, array.splice(previousIndex, 1)[0]);
10 | return array;
11 | }
12 |
13 | export const events = {
14 | start: ['touchstart', 'mousedown'],
15 | move: ['touchmove', 'mousemove'],
16 | end: ['touchend', 'touchcancel', 'mouseup'],
17 | };
18 |
19 | export const vendorPrefix = (function() {
20 | if (typeof window === 'undefined' || typeof document === 'undefined') return ''; // server environment
21 | // fix for:
22 | // https://bugzilla.mozilla.org/show_bug.cgi?id=548397
23 | // window.getComputedStyle() returns null inside an iframe with display: none
24 | // in this case return an array with a fake mozilla style in it.
25 | const styles = window.getComputedStyle(document.documentElement, '') || ['-moz-hidden-iframe'];
26 | const pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1];
27 |
28 | switch (pre) {
29 | case 'ms':
30 | return 'ms';
31 | default:
32 | return pre && pre.length ? pre[0].toUpperCase() + pre.substr(1) : '';
33 | }
34 | })();
35 |
36 | export function closest(el, fn) {
37 | while (el) {
38 | if (fn(el)) return el;
39 | el = el.parentNode;
40 | }
41 | }
42 |
43 | export function limit(min, max, value) {
44 | if (value < min) {
45 | return min;
46 | }
47 | if (value > max) {
48 | return max;
49 | }
50 | return value;
51 | }
52 |
53 | function getCSSPixelValue(stringValue) {
54 | if (stringValue.substr(-2) === 'px') {
55 | return parseFloat(stringValue);
56 | }
57 | return 0;
58 | }
59 |
60 | export function getElementMargin(element) {
61 | const style = window.getComputedStyle(element);
62 |
63 | return {
64 | top: getCSSPixelValue(style.marginTop),
65 | right: getCSSPixelValue(style.marginRight),
66 | bottom: getCSSPixelValue(style.marginBottom),
67 | left: getCSSPixelValue(style.marginLeft),
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-drag2",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "yanyalong",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js",
11 | "lint": "eslint --ext .js,.vue src"
12 | },
13 | "dependencies": {
14 | "ajv": "^6.0.0",
15 | "vue": "^2.5.2",
16 | "vue-router": "^3.0.1",
17 | "vue-slicksort": "^0.1.10",
18 | "vuex": "^3.0.1"
19 | },
20 | "devDependencies": {
21 | "autoprefixer": "^7.1.2",
22 | "babel-core": "^6.22.1",
23 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
24 | "babel-loader": "^7.1.1",
25 | "babel-plugin-syntax-jsx": "^6.18.0",
26 | "babel-plugin-transform-runtime": "^6.22.0",
27 | "babel-plugin-transform-vue-jsx": "^3.5.0",
28 | "babel-polyfill": "^6.26.0",
29 | "babel-preset-env": "^1.3.2",
30 | "babel-preset-stage-2": "^6.22.0",
31 | "chalk": "^2.0.1",
32 | "copy-webpack-plugin": "^4.0.1",
33 | "css-loader": "^0.28.0",
34 | "extract-text-webpack-plugin": "^3.0.0",
35 | "file-loader": "^1.1.4",
36 | "friendly-errors-webpack-plugin": "^1.6.1",
37 | "html-webpack-plugin": "^2.30.1",
38 | "node-notifier": "^5.1.2",
39 | "node-sass": "^4.9.3",
40 | "optimize-css-assets-webpack-plugin": "^3.2.0",
41 | "ora": "^1.2.0",
42 | "portfinder": "^1.0.13",
43 | "postcss-import": "^11.0.0",
44 | "postcss-loader": "^2.0.8",
45 | "postcss-url": "^7.2.1",
46 | "rimraf": "^2.6.0",
47 | "sass-loader": "^7.1.0",
48 | "semver": "^5.3.0",
49 | "shelljs": "^0.7.6",
50 | "uglifyjs-webpack-plugin": "^1.1.1",
51 | "url-loader": "^1.1.1",
52 | "vue-loader": "^13.3.0",
53 | "vue-style-loader": "^3.0.1",
54 | "vue-template-compiler": "^2.5.2",
55 | "webpack": "^3.6.0",
56 | "webpack-bundle-analyzer": "^2.9.0",
57 | "webpack-dev-server": "^2.9.1",
58 | "webpack-merge": "^4.1.0"
59 | },
60 | "engines": {
61 | "node": ">= 6.0.0",
62 | "npm": ">= 3.0.0"
63 | },
64 | "browserslist": [
65 | "> 1%",
66 | "last 2 versions",
67 | "not ie <= 8"
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | handele bar
5 |
6 |
7 |
8 |
9 |
10 |
11 | 不气温
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
71 |
72 |
73 |
100 |
--------------------------------------------------------------------------------
/src/components/dragItem/showDetails.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
56 |
57 |
58 |
73 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: true,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'cheap-module-eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | cssSourceMap: true
44 | },
45 |
46 | build: {
47 | // Template for index.html
48 | index: path.resolve(__dirname, '../dist/index.html'),
49 |
50 | // Paths
51 | assetsRoot: path.resolve(__dirname, '../dist'),
52 | assetsSubDirectory: 'static',
53 | assetsPublicPath: '/',
54 |
55 | /**
56 | * Source Maps
57 | */
58 |
59 | productionSourceMap: true,
60 | // https://webpack.js.org/configuration/devtool/#production
61 | devtool: '#source-map',
62 |
63 | // Gzip off by default as many popular static hosts such as
64 | // Surge or Netlify already gzip all static assets for you.
65 | // Before setting to `true`, make sure to:
66 | // npm install --save-dev compression-webpack-plugin
67 | productionGzip: false,
68 | productionGzipExtensions: ['js', 'css'],
69 |
70 | // Run the build command with an extra argument to
71 | // View the bundle analyzer report after build finishes:
72 | // `npm run build --report`
73 | // Set to `true` or `false` to always turn it on or off
74 | bundleAnalyzerReport: process.env.npm_config_report
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | dragDemo
4 |
5 | vueSlicksortPractice
6 |
7 | canBack
8 |
9 |
10 |
11 |
33 |
34 |
122 |
--------------------------------------------------------------------------------
/src/components/dragItem/twoColumns.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
74 |
75 |
76 |
107 |
--------------------------------------------------------------------------------
/src/components/dragItem/tableLayout.vue:
--------------------------------------------------------------------------------
1 | // 这个是为了备份用的
2 | // 这个是为了备份用的
3 | // 这个是为了备份用的
4 | // 这个是为了备份用的
5 | // 这个是为了备份用的
6 | // 这个是为了备份用的
7 | // 这个是为了备份用的
8 | // 这个是为了备份用的
9 | // 这个是为了备份用的
10 | // 这个是为了备份用的
11 |
12 |
13 |
14 | {{item.text}}
15 |
16 |
17 |
18 |
124 |
125 |
126 |
134 |
--------------------------------------------------------------------------------
/src/components/dragItem/single.备份.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
往上移动了
5 |
10 |
11 | {{ index }} : {{ site.name }}
12 |
13 |
14 |
往下移动了
15 |
16 |
17 |
18 |
145 |
146 |
147 |
155 |
--------------------------------------------------------------------------------
/src/components/dragList/dragCompent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{item.title}}
4 |
5 |
6 |
7 |
144 |
145 |
146 |
158 |
--------------------------------------------------------------------------------
/src/view/dragDemo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
表单布局
5 |
6 |
7 |
8 | {{cloneLeftItemText}}
9 |
10 |
11 |
21 |
22 |
23 |
常用控件 demo
24 |
34 |
35 |
36 |
表单标题
37 |
38 |
43 |
44 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
139 |
140 |
201 |
--------------------------------------------------------------------------------
/src/components/dragItem/children.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
29 |
30 |
34 |
35 |
36 |
60 |
61 |
62 |
305 |
306 |
307 |
330 |
--------------------------------------------------------------------------------
/src/components/dragItem/single.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
31 |
267 |
268 |
269 |
354 |
--------------------------------------------------------------------------------
/src/store/modules/dragItemDate.js:
--------------------------------------------------------------------------------
1 | import { stat } from "fs";
2 |
3 | const state = {
4 | // 只用了原数据的数组 对象里增加了 'upActive': false 'downActive': false 是用来控制红色边框的出现
5 | layoutContentItem: [{
6 | "componentKey": "Text",
7 | "title": "文本000",
8 | "fieldId": "20181108195717mCmp5TOBfA",
9 | "inLeft": false,
10 | "required": false,
11 | "crux": true,
12 | "isTextArea": false,
13 | "size": "large",
14 | "borderColor": "",
15 | "bacColor": "",
16 | "titleFontColor": "",
17 | "titleFontSize": 13,
18 | "titleBold": false,
19 | "titleUnderline": false,
20 | "bacImg": null,
21 | "tips": "",
22 | "hideTitle": false,
23 | "uniqueCheck": false,
24 | "visible": false,
25 | "calculate": false,
26 | "calType": "2",
27 | "complexCal": "",
28 | "complexTrueVal": "",
29 | "numberFields": "",
30 | "numberFieldArr": [],
31 | "columncode": "wb_1541678285948866",
32 | 'upActive': false,
33 | 'downActive': false
34 | },
35 | {
36 | "componentKey": "Text",
37 | "title": "文本222",
38 | "fieldId": "20181109101800TWTeNtI5xk",
39 | "inLeft": true,
40 | "required": true,
41 | "crux": false,
42 | "isTextArea": false,
43 | "size": "large",
44 | "borderColor": "",
45 | "bacColor": "",
46 | "titleFontColor": "",
47 | "titleFontSize": 13,
48 | "titleBold": false,
49 | "titleUnderline": false,
50 | "bacImg": null,
51 | "tips": "",
52 | "hideTitle": true,
53 | "uniqueCheck": false,
54 | "visible": false,
55 | "calculate": false,
56 | "calType": "2",
57 | "complexCal": "",
58 | "complexTrueVal": "",
59 | "numberFields": "",
60 | "numberFieldArr": [],
61 | "columncode": "wb222_1541729905916187",
62 | 'upActive': false,
63 | 'downActive': false
64 | },
65 | {
66 | "componentKey": "Text",
67 | "title": "文本3333",
68 | "fieldId": "20181109101800TWTeNtI5xk",
69 | "inLeft": false,
70 | "required": false,
71 | "crux": false,
72 | "isTextArea": false,
73 | "size": "large",
74 | "borderColor": "",
75 | "bacColor": "",
76 | "titleFontColor": "",
77 | "titleFontSize": 13,
78 | "titleBold": false,
79 | "titleUnderline": false,
80 | "bacImg": null,
81 | "tips": "",
82 | "hideTitle": false,
83 | "uniqueCheck": false,
84 | "visible": false,
85 | "calculate": false,
86 | "calType": "2",
87 | "complexCal": "",
88 | "complexTrueVal": "",
89 | "numberFields": "",
90 | "numberFieldArr": [],
91 | "columncode": "wb222_1541729905916187",
92 | 'upActive': false,
93 | 'downActive': false
94 | },
95 | // {
96 | // "fieldId": "20181108201139GBJRyIJAt2",
97 | // "componentKey": "ColumnPanel",
98 | // "title": "一行两列",
99 | // "size": "2",
100 | // "available": true,
101 | // "bacColor": "",
102 | // "borderWidth": 1,
103 | // "layoutDetail": [{
104 | // "componentKey": "ColumnPanel",
105 | // "layoutDetail": [],
106 | // "size": "1"
107 | // }, {
108 | // "componentKey": "ColumnPanel",
109 | // "layoutDetail": [{
110 | // "componentKey": "Text",
111 | // "title": "文本111",
112 | // "fieldId": "20181108201815UbNRaXUIfB",
113 | // "inLeft": false,
114 | // "required": false,
115 | // "crux": false,
116 | // "isTextArea": false,
117 | // "size": "large",
118 | // "borderColor": "",
119 | // "bacColor": "",
120 | // "titleFontColor": "",
121 | // "titleFontSize": 13,
122 | // "titleBold": false,
123 | // "titleUnderline": false,
124 | // "bacImg": null,
125 | // "tips": "",
126 | // "hideTitle": false,
127 | // "uniqueCheck": false,
128 | // "visible": false,
129 | // "calculate": false,
130 | // "calType": "2",
131 | // "complexCal": "",
132 | // "complexTrueVal": "",
133 | // "numberFields": "",
134 | // "numberFieldArr": [],
135 | // "columncode": "wb_1541679666395165"
136 | // }],
137 | // "size": "1"
138 | // }]
139 | // },
140 | //
141 | // {
142 | // "fieldId": "20181109101802zMgVW3gcEU",
143 | // "componentKey": "ColumnPanel",
144 | // "title": "一行两列",
145 | // "size": "2",
146 | // "available": true,
147 | // "bacColor": "",
148 | // "borderWidth": 1,
149 | // "layoutDetail": [{
150 | // "componentKey": "ColumnPanel",
151 | // "layoutDetail": [{
152 | // "componentKey": "Password",
153 | // "title": "密码",
154 | // "fieldId": "20181109101809alDirc1F1m",
155 | // "inLeft": false,
156 | // "required": false,
157 | // "crux": false,
158 | // "isTextArea": false,
159 | // "size": "large",
160 | // "borderColor": "",
161 | // "bacColor": "",
162 | // "titleFontColor": "",
163 | // "titleFontSize": 13,
164 | // "titleBold": false,
165 | // "titleUnderline": false,
166 | // "bacImg": null,
167 | // "tips": "",
168 | // "hideTitle": false,
169 | // "uniqueCheck": false,
170 | // "visible": false,
171 | // "calculate": false,
172 | // "calType": "2",
173 | // "complexCal": "",
174 | // "complexTrueVal": "",
175 | // "numberFields": "",
176 | // "numberFieldArr": [],
177 | // "columncode": "mm_1541729905915453"
178 | // }],
179 | // "size": "1"
180 | // }, {
181 | // "componentKey": "ColumnPanel",
182 | // "layoutDetail": [{
183 | // "componentKey": "Text",
184 | // "title": "2112",
185 | // "fieldId": "20181109101802omluIDs3TQ",
186 | // "inLeft": false,
187 | // "required": false,
188 | // "crux": false,
189 | // "isTextArea": false,
190 | // "size": "large",
191 | // "borderColor": "",
192 | // "bacColor": "",
193 | // "titleFontColor": "",
194 | // "titleFontSize": 13,
195 | // "titleBold": false,
196 | // "titleUnderline": false,
197 | // "bacImg": null,
198 | // "tips": "",
199 | // "hideTitle": false,
200 | // "uniqueCheck": false,
201 | // "visible": false,
202 | // "calculate": false,
203 | // "calType": "2",
204 | // "complexCal": "",
205 | // "complexTrueVal": "",
206 | // "numberFields": "",
207 | // "numberFieldArr": [],
208 | // "columncode": "n2112_1541729905915431"
209 | // }],
210 | // "size": "1"
211 | // }]
212 | // }
213 | ],
214 | // layoutContentItem: [{text: '1111', upActive: false, downActive: false}, {text: '2222', upActive: false, downActive: false}, {text: '3333', upActive: false, downActive: false}, {text: '44444', upActive: false, downActive: false}, {text: '55555', upActive: false, downActive: false}],
215 | // layoutContentItem: [{text: '1111', upActive: false, downActive: false}],
216 | initPositionY: 999, // 中部 开始拖动组件的 index
217 | positionY: 999, // 进入到哪个组件的 index
218 | itemIsMoving: false, // 中间布局的item 是否被拖动
219 | leftDragItemIsMoving: false, // 左侧的item是否被拖动
220 | centerDraggingItemData: '', // 保存中间拖动的组件的数据
221 | leftDraggingItemData: '', // 保存左侧拖动的组件的数据
222 | isNeedUpdateDate: false // 是否需要更新数据
223 | };
224 |
225 | const actions = {
226 | // item = {index:1, position: 1} position 1 是上 2 是下
227 | updateLayoutContentItem({ commit }, item) {
228 | if (item) {
229 | var layoutContentItemLength = state.layoutContentItem.length - 1
230 | if (item.index > layoutContentItemLength) {
231 | item.index = layoutContentItemLength
232 | }
233 | commit('LAYOUT_CONTENT_ITEM', item);
234 | }
235 | },
236 | changeLayoutContentItem({ commit }) {
237 | commit('CHANGE_LAYOUT_CONTENT_ITEM');
238 | },
239 | updatePositionY({ commit }, position) {
240 | // var layoutContentItemLength = state.layoutContentItem.length - 1
241 | // if (position > layoutContentItemLength) {
242 | // position = layoutContentItemLength
243 | // }
244 | commit('UPDATE_POSITION_Y', position);
245 | },
246 | updateInitPositionY({ commit }, position) {
247 | commit('UPDATE_INIT_POSITION_Y', position);
248 | },
249 | updateItemIsMoving({ commit }, bool) {
250 | commit('UPDATE_ITEM_IS_MOVING', bool);
251 | },
252 | updateLeftDragItemIsMoving({commit}, bool) {
253 | commit('UPDATE_LEFT_DRAG_ITEM_ISMOVING', bool);
254 | },
255 | // 中间拖动的组件的数据
256 | updateCenterDraggingItemData({commit}, item) {
257 | commit('UPDATE_CENTER_DRAGGING_ITEM_DATA', item);
258 | },
259 | // 左边拖动的组件的数据
260 | updateLeftDraggingItemData({commit}, item) {
261 | commit('UPDATE_LEFT_DRAGGING_ITEM_DATA', item);
262 | },
263 | // 是否需要触发更新数据
264 | updateIsNeedUpdateDate({commit}, bool) {
265 | commit('UPDATE_IS_NEED_UPDATE_DATA', bool);
266 | }
267 | };
268 |
269 | const getters = {
270 | // loginStatus: state => state.loginStatus,
271 | layoutContentItem: state => state.layoutContentItem,
272 | positionY: state => state.positionY,
273 | itemIsMoving: state => state.itemIsMoving,
274 | leftDragItemIsMoving: state => state.leftDragItemIsMoving,
275 | centerDraggingItemData: state => state.centerDraggingItemData,
276 | leftDraggingItemData: state => state.leftDraggingItemData
277 | };
278 |
279 | const mutations = {
280 | // [types.SET_USER_INFO](state, userInfo) {
281 | // state.userInfo = userInfo;
282 | // }
283 | ['LAYOUT_CONTENT_ITEM'](state, item) {
284 | var index = item.index
285 | var data = state.layoutContentItem
286 | // 清空初始化
287 | for (var i = 0; i < data.length; i++) {
288 | if (data[i].downActive) {
289 | data[i].downActive = false
290 | }
291 | if (data[i].upActive) {
292 | data[i].upActive = false
293 | }
294 | }
295 | // console.log(item.position)
296 | if (item.position === 1 ) {
297 | data[index].upActive = true
298 | data[index].downActive = false
299 | } else {
300 | data[index].downActive = true
301 | data[index].upActive = false
302 | }
303 | state.layoutContentItem = data
304 | // console.log(data)
305 | },
306 | ['UPDATE_POSITION_Y'](state, position) {
307 | state.positionY = position
308 | },
309 | ['UPDATE_INIT_POSITION_Y'](state, position) {
310 | state.initPositionY = position
311 | },
312 | ['UPDATE_ITEM_IS_MOVING'](state, bool) {
313 | state.itemIsMoving = bool
314 | },
315 | ['CHANGE_LAYOUT_CONTENT_ITEM'](state) {
316 | // 拖动中间的组件
317 | if (state.centerDraggingItemData) {
318 | var isNeedUpdateDateIndex = ''
319 | for (var i = 0; i < state.layoutContentItem.length; i++) {
320 | if (state.layoutContentItem[i].upActive || state.layoutContentItem[i].downActive) {
321 | isNeedUpdateDateIndex = i
322 | }
323 | }
324 | // 这是拖动 临界的位置 在临界位置
325 | if (isNeedUpdateDateIndex == state.initPositionY) {
326 | state.isNeedUpdateDate = false
327 | }
328 | // 可以触发更新
329 | if (state.isNeedUpdateDate) {
330 | var data1 = state.layoutContentItem
331 | data1.splice(state.initPositionY, 1)
332 | state.layoutContentItem = data1
333 | var data2 = state.layoutContentItem
334 | for (let i = 0; i < data2.length; i++) {
335 | let upActive = data2[i].upActive
336 | let downActive = data2[i].downActive
337 | if (upActive) {
338 | state.centerDraggingItemData.upActive = false
339 | state.centerDraggingItemData.downActive = false
340 | data2.splice(i, 0, state.centerDraggingItemData)
341 | // 更新 positionY
342 | state.positionY = i
343 | break
344 | }
345 | if (downActive) {
346 | state.centerDraggingItemData.upActive = false
347 | state.centerDraggingItemData.downActive = false
348 | data2.splice(i + 1, 0, state.centerDraggingItemData)
349 | // 更新 positionY
350 | state.positionY = i + 1
351 | break
352 | }
353 | }
354 | state.layoutContentItem = data2
355 | }
356 | var data3 = state.layoutContentItem
357 | for (let i = 0; i < data3.length; i ++) {
358 | data3[i].downActive = false
359 | data3[i].upActive = false
360 | }
361 | state.layoutContentItem = data3
362 | }
363 | // 拖动左侧的组件
364 | if (state.leftDraggingItemData) {
365 | var title = state.leftDraggingItemData.title
366 | var obj = {}
367 | obj['title'] = title
368 | obj['upActive'] = false
369 | obj['downActive'] = false
370 | var data = state.layoutContentItem
371 | for (let i = 0; i < data.length; i++) {
372 | let upActive = data[i].upActive
373 | let downActive = data[i].downActive
374 | if (upActive) {
375 | data.splice(i, 0, obj)
376 | // 更新 positionY
377 | state.positionY = i
378 | break
379 | }
380 | if (downActive) {
381 | data.splice(i + 1, 0, obj)
382 | state.positionY = i + 1
383 | break
384 | }
385 | }
386 | for (let i = 0; i < data.length; i ++) {
387 | data[i].downActive = false
388 | data[i].upActive = false
389 | }
390 | state.layoutContentItem = data
391 | }
392 | },
393 | ['UPDATE_LEFT_DRAG_ITEM_ISMOVING'](state, bool) {
394 | state.leftDragItemIsMoving = bool
395 | },
396 | ['UPDATE_CENTER_DRAGGING_ITEM_DATA'](state, item) {
397 | state.centerDraggingItemData = item
398 | },
399 | ['UPDATE_LEFT_DRAGGING_ITEM_DATA'](state, item) {
400 | state.leftDraggingItemData = item
401 | // console.log(state.leftDraggingItemData)
402 | },
403 | ['UPDATE_IS_NEED_UPDATE_DATA'](state, bool) {
404 | state.isNeedUpdateDate = bool
405 | }
406 | };
407 |
408 | export default {
409 | state,
410 | actions,
411 | getters,
412 | mutations,
413 | };
414 |
--------------------------------------------------------------------------------
/src/components/vue-slicksort/ContainerMixin.js:
--------------------------------------------------------------------------------
1 | import Manager from './Manager';
2 | import {
3 | closest,
4 | events,
5 | vendorPrefix,
6 | limit,
7 | getElementMargin,
8 | arrayMove,
9 | } from './utils';
10 |
11 | // Export Sortable Container Component Mixin
12 | export const ContainerMixin = {
13 | data() {
14 | return {
15 | sorting: false,
16 | sortingIndex: null,
17 | manager: new Manager(),
18 | events: {
19 | start: this.handleStart,
20 | move: this.handleMove,
21 | end: this.handleEnd,
22 | },
23 | };
24 | },
25 |
26 | props: {
27 | value: { type: Array, required: true },
28 | axis: { type: String, default: 'y' }, // 'x', 'y', 'xy'
29 | distance: { type: Number, default: 0 },
30 | pressDelay: { type: Number, default: 0 },
31 | pressThreshold: { type: Number, default: 5 },
32 | useDragHandle: { type: Boolean, default: false },
33 | useWindowAsScrollContainer: { type: Boolean, default: false },
34 | hideSortableGhost: { type: Boolean, default: true },
35 | lockToContainerEdges: { type: Boolean, default: false },
36 | lockOffset: { type: [String, Number, Array], default: '50%' },
37 | transitionDuration: { type: Number, default: 300 },
38 | lockAxis: String,
39 | helperClass: String,
40 | contentWindow: Object,
41 | shouldCancelStart: {
42 | type: Function,
43 | default: (e) => {
44 | // Cancel sorting if the event target is an `input`, `textarea`, `select` or `option`
45 | const disabledElements = ['input', 'textarea', 'select', 'option', 'button'];
46 | return disabledElements.indexOf(e.target.tagName.toLowerCase()) !== -1;
47 | },
48 | },
49 | getHelperDimensions: {
50 | type: Function,
51 | default: ({node}) => ({
52 | width: node.offsetWidth,
53 | height: node.offsetHeight,
54 | }),
55 | },
56 | },
57 |
58 | provide() {
59 | return {
60 | manager: this.manager,
61 | };
62 | },
63 |
64 | mounted() {
65 | this.container = this.$el;
66 | this.document = this.container.ownerDocument || document;
67 | this._window = this.contentWindow || window;
68 | this.scrollContainer = this.useWindowAsScrollContainer
69 | ? this.document.body
70 | : this.container;
71 |
72 | for (const key in this.events) {
73 | if (this.events.hasOwnProperty(key)) {
74 | events[key].forEach(eventName =>
75 | this.container.addEventListener(eventName, this.events[key], false)
76 | // console.log(eventName + this.events[key])
77 | );
78 | }
79 | }
80 | },
81 |
82 | beforeDestroy() {
83 | for (const key in this.events) {
84 | if (this.events.hasOwnProperty(key)) {
85 | events[key].forEach(eventName =>
86 | this.container.removeEventListener(eventName, this.events[key])
87 | );
88 | }
89 | }
90 | },
91 |
92 | methods: {
93 |
94 | handleStart(e) {
95 | const {distance, shouldCancelStart} = this.$props;
96 |
97 | if (e.button === 2 || shouldCancelStart(e)) {
98 | return false;
99 | }
100 |
101 | this._touched = true;
102 | this._pos = {
103 | x: e.pageX,
104 | y: e.pageY,
105 | };
106 |
107 | const node = closest(e.target, el => el.sortableInfo != null);
108 |
109 | if (
110 | node &&
111 | node.sortableInfo &&
112 | this.nodeIsChild(node) &&
113 | !this.sorting
114 | ) {
115 | const {useDragHandle} = this.$props;
116 | const {index, collection} = node.sortableInfo;
117 |
118 | if (
119 | useDragHandle && !closest(e.target, el => el.sortableHandle != null)
120 | )
121 | return;
122 |
123 | this.manager.active = {index, collection};
124 |
125 | /*
126 | * Fixes a bug in Firefox where the :active state of anchor tags
127 | * prevent subsequent 'mousemove' events from being fired
128 | * (see https://github.com/clauderic/react-sortable-hoc/issues/118)
129 | */
130 | if (e.target.tagName.toLowerCase() === 'a') {
131 | e.preventDefault();
132 | }
133 | // console.log(distance)
134 | if (!distance) {
135 | if (this.$props.pressDelay === 0) {
136 | this.handlePress(e);
137 | } else {
138 | this.pressTimer = setTimeout(
139 | () => this.handlePress(e),
140 | this.$props.pressDelay
141 | );
142 | }
143 | }
144 | }
145 | // console.log('handleStart')
146 | },
147 |
148 | nodeIsChild(node) {
149 | return node.sortableInfo.manager === this.manager;
150 | },
151 |
152 | handleMove(e) {
153 | const {distance, pressThreshold} = this.$props;
154 |
155 | if (!this.sorting && this._touched) {
156 | this._delta = {
157 | x: this._pos.x - e.pageX,
158 | y: this._pos.y - e.pageY,
159 | };
160 | const delta = Math.abs(this._delta.x) + Math.abs(this._delta.y);
161 |
162 | if (!distance && (!pressThreshold || pressThreshold && delta >= pressThreshold)) {
163 | clearTimeout(this.cancelTimer);
164 | this.cancelTimer = setTimeout(this.cancel, 0);
165 | } else if (distance && delta >= distance && this.manager.isActive()) {
166 | this.handlePress(e);
167 | }
168 | }
169 | // console.log('handleMove')
170 | },
171 |
172 | handleEnd() {
173 | const {distance} = this.$props;
174 |
175 | this._touched = false;
176 |
177 | if (!distance) {
178 | this.cancel();
179 | }
180 | // console.log('handleEnd')
181 | },
182 |
183 | cancel() {
184 | if (!this.sorting) {
185 | clearTimeout(this.pressTimer);
186 | this.manager.active = null;
187 | }
188 | },
189 |
190 | handlePress(e) {
191 | const active = this.manager.getActive();
192 |
193 | if (active) {
194 | const {
195 | axis,
196 | getHelperDimensions,
197 | helperClass,
198 | hideSortableGhost,
199 | useWindowAsScrollContainer,
200 | } = this.$props;
201 | const {node, collection} = active;
202 | const {index} = node.sortableInfo;
203 | const margin = getElementMargin(node);
204 |
205 | const containerBoundingRect = this.container.getBoundingClientRect();
206 | const dimensions = getHelperDimensions({index, node, collection});
207 |
208 | this.node = node;
209 | this.margin = margin;
210 | this.width = dimensions.width;
211 | this.height = dimensions.height;
212 | this.marginOffset = {
213 | x: this.margin.left + this.margin.right,
214 | y: Math.max(this.margin.top, this.margin.bottom),
215 | };
216 | this.boundingClientRect = node.getBoundingClientRect();
217 | this.containerBoundingRect = containerBoundingRect;
218 | this.index = index;
219 | this.newIndex = index;
220 |
221 | this._axis = {
222 | x: axis.indexOf('x') >= 0,
223 | y: axis.indexOf('y') >= 0,
224 | };
225 | this.offsetEdge = this.getEdgeOffset(node);
226 | this.initialOffset = this.getOffset(e);
227 | this.initialScroll = {
228 | top: this.scrollContainer.scrollTop,
229 | left: this.scrollContainer.scrollLeft,
230 | };
231 |
232 | this.initialWindowScroll = {
233 | top: window.pageYOffset,
234 | left: window.pageXOffset,
235 | };
236 |
237 | const fields = node.querySelectorAll('input, textarea, select');
238 | const clonedNode = node.cloneNode(true);
239 | const clonedFields = [
240 | ...clonedNode.querySelectorAll('input, textarea, select'),
241 | ]; // Convert NodeList to Array
242 |
243 | clonedFields.forEach((field, index) => {
244 | if (field.type !== 'file' && fields[index]) {
245 | field.value = fields[index].value;
246 | }
247 | });
248 |
249 | this.helper = this.document.body.appendChild(clonedNode);
250 | // console.log(clonedNode)
251 | this.helper.style.position = 'fixed';
252 | this.helper.style.top = `${this.boundingClientRect.top - margin.top}px`;
253 | this.helper.style.left = `${this.boundingClientRect.left - margin.left}px`;
254 | this.helper.style.width = `${this.width}px`;
255 | this.helper.style.height = `${this.height}px`;
256 | this.helper.style.boxSizing = 'border-box';
257 | this.helper.style.pointerEvents = 'none';
258 |
259 | if (hideSortableGhost) {
260 | this.sortableGhost = node;
261 | // yyl
262 | node.style.visibility = 'hidden';
263 | node.style.opacity = 0;
264 | // node.style.background = 'red';
265 | }
266 |
267 | this.minTranslate = {};
268 | this.maxTranslate = {};
269 | if (this._axis.x) {
270 | this.minTranslate.x = (useWindowAsScrollContainer
271 | ? 0
272 | : containerBoundingRect.left) -
273 | this.boundingClientRect.left -
274 | this.width / 2;
275 | this.maxTranslate.x = (useWindowAsScrollContainer
276 | ? this._window.innerWidth
277 | : containerBoundingRect.left + containerBoundingRect.width) -
278 | this.boundingClientRect.left -
279 | this.width / 2;
280 | }
281 | if (this._axis.y) {
282 | this.minTranslate.y = (useWindowAsScrollContainer
283 | ? 0
284 | : containerBoundingRect.top) -
285 | this.boundingClientRect.top -
286 | this.height / 2;
287 | this.maxTranslate.y = (useWindowAsScrollContainer
288 | ? this._window.innerHeight
289 | : containerBoundingRect.top + containerBoundingRect.height) -
290 | this.boundingClientRect.top -
291 | this.height / 2;
292 | }
293 |
294 | if (helperClass) {
295 | this.helper.classList.add(...helperClass.split(' '));
296 | }
297 |
298 | this.listenerNode = e.touches ? node : this._window;
299 |
300 | // console.log(events.move)
301 | // ["touchmove", "mousemove"]
302 | events.move.forEach(eventName =>
303 | this.listenerNode.addEventListener(
304 | eventName,
305 | this.handleSortMove,
306 | false
307 | ));
308 |
309 | // console.log(events.end)
310 | // ["touchend", "touchcancel", "mouseup"]
311 |
312 | events.end.forEach(eventName =>
313 | this.listenerNode.addEventListener(
314 | eventName,
315 | this.handleSortEnd,
316 | false
317 | ));
318 |
319 | this.sorting = true;
320 | this.sortingIndex = index;
321 |
322 | // this.$emit('sortStart', {event: e, node, index, collection});
323 | // console.log('handlePress')
324 | }
325 | },
326 |
327 | handleSortMove(e) {
328 | e.preventDefault(); // Prevent scrolling on mobile
329 |
330 | // 这是移动节点 yyl
331 | this.updatePosition(e);
332 | this.animateNodes();
333 | // this.autoscroll();
334 |
335 | // this.$emit('sortMove', { event: e });
336 | },
337 |
338 | handleSortEnd(e) {
339 | const {collection} = this.manager.active;
340 |
341 | // Remove the event listeners if the node is still in the DOM
342 | if (this.listenerNode) {
343 | events.move.forEach(eventName =>
344 | this.listenerNode.removeEventListener(
345 | eventName,
346 | this.handleSortMove
347 | ));
348 | events.end.forEach(eventName =>
349 | this.listenerNode.removeEventListener(eventName, this.handleSortEnd));
350 | }
351 |
352 | // Remove the helper from the DOM
353 | this.helper.parentNode.removeChild(this.helper);
354 |
355 | if (this.hideSortableGhost && this.sortableGhost) {
356 | // yyl
357 | this.sortableGhost.style.visibility = '';
358 | this.sortableGhost.style.opacity = '';
359 | // this.sortableGhost.style.background = '';
360 | }
361 |
362 | const nodes = this.manager.refs[collection];
363 | for (let i = 0, len = nodes.length; i < len; i++) {
364 | const node = nodes[i];
365 | const el = node.node;
366 |
367 | // Clear the cached offsetTop / offsetLeft value
368 | node.edgeOffset = null;
369 |
370 | // Remove the transforms / transitions
371 | el.style[`${vendorPrefix}Transform`] = '';
372 | el.style[`${vendorPrefix}TransitionDuration`] = '';
373 | }
374 |
375 | // Stop autoscroll
376 | clearInterval(this.autoscrollInterval);
377 | this.autoscrollInterval = null;
378 |
379 | // Update state
380 | this.manager.active = null;
381 |
382 | this.sorting = false;
383 | this.sortingIndex = null;
384 |
385 | this.$emit('sortEnd', {
386 | event: e,
387 | oldIndex: this.index,
388 | newIndex: this.newIndex,
389 | collection,
390 | });
391 | this.$emit('input', arrayMove(this.value, this.index, this.newIndex));
392 |
393 | this._touched = false;
394 | },
395 |
396 | getEdgeOffset(node, offset = {top: 0, left: 0}) {
397 | // Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested
398 | if (node) {
399 | const nodeOffset = {
400 | top: offset.top + node.offsetTop,
401 | left: offset.left + node.offsetLeft,
402 | };
403 | if (node.parentNode !== this.container) {
404 | return this.getEdgeOffset(node.parentNode, nodeOffset);
405 | } else {
406 | return nodeOffset;
407 | }
408 | }
409 | },
410 |
411 | getOffset(e) {
412 | return {
413 | x: e.touches ? e.touches[0].pageX : e.pageX,
414 | y: e.touches ? e.touches[0].pageY : e.pageY,
415 | };
416 | },
417 |
418 | getLockPixelOffsets() {
419 | let {lockOffset} = this.$props;
420 |
421 | if (!Array.isArray(this.lockOffset)) {
422 | lockOffset = [lockOffset, lockOffset];
423 | }
424 |
425 | if (lockOffset.length !== 2) {
426 | throw new Error(`lockOffset prop of SortableContainer should be a single value or an array of exactly two values. Given ${lockOffset}`);
427 | }
428 |
429 | const [minLockOffset, maxLockOffset] = lockOffset;
430 |
431 | return [
432 | this.getLockPixelOffset(minLockOffset),
433 | this.getLockPixelOffset(maxLockOffset),
434 | ];
435 | },
436 |
437 | getLockPixelOffset(lockOffset) {
438 | let offsetX = lockOffset;
439 | let offsetY = lockOffset;
440 | let unit = 'px';
441 |
442 | if (typeof lockOffset === 'string') {
443 | const match = /^[+-]?\d*(?:\.\d*)?(px|%)$/.exec(lockOffset);
444 |
445 | if (match === null) {
446 | throw new Error(`lockOffset value should be a number or a string of a number followed by "px" or "%". Given ${lockOffset}`);
447 | }
448 |
449 | offsetX = (offsetY = parseFloat(lockOffset));
450 | unit = match[1];
451 | }
452 |
453 | if (!isFinite(offsetX) || !isFinite(offsetY)) {
454 | throw new Error(`lockOffset value should be a finite. Given ${lockOffset}`);
455 | }
456 |
457 | if (unit === '%') {
458 | offsetX = offsetX * this.width / 100;
459 | offsetY = offsetY * this.height / 100;
460 | }
461 |
462 | return {
463 | x: offsetX,
464 | y: offsetY,
465 | };
466 | },
467 |
468 | updatePosition(e) {
469 | const {lockAxis, lockToContainerEdges} = this.$props;
470 |
471 | const offset = this.getOffset(e);
472 | const translate = {
473 | x: offset.x - this.initialOffset.x,
474 | y: offset.y - this.initialOffset.y,
475 | };
476 | // Adjust for window scroll
477 | translate.y -= (window.pageYOffset - this.initialWindowScroll.top);
478 | translate.x -= (window.pageXOffset - this.initialWindowScroll.left);
479 |
480 | this.translate = translate;
481 |
482 | if (lockToContainerEdges) {
483 | const [minLockOffset, maxLockOffset] = this.getLockPixelOffsets();
484 | const minOffset = {
485 | x: this.width / 2 - minLockOffset.x,
486 | y: this.height / 2 - minLockOffset.y,
487 | };
488 | const maxOffset = {
489 | x: this.width / 2 - maxLockOffset.x,
490 | y: this.height / 2 - maxLockOffset.y,
491 | };
492 |
493 | translate.x = limit(
494 | this.minTranslate.x + minOffset.x,
495 | this.maxTranslate.x - maxOffset.x,
496 | translate.x
497 | );
498 | translate.y = limit(
499 | this.minTranslate.y + minOffset.y,
500 | this.maxTranslate.y - maxOffset.y,
501 | translate.y
502 | );
503 | }
504 |
505 | if (lockAxis === 'x') {
506 | translate.y = 0;
507 | } else if (lockAxis === 'y') {
508 | translate.x = 0;
509 | }
510 |
511 | this.helper.style[
512 | `${vendorPrefix}Transform`
513 | ] = `translate3d(${translate.x}px,${translate.y}px, 0)`;
514 | },
515 |
516 | animateNodes() {
517 | const {transitionDuration, hideSortableGhost} = this.$props;
518 | const nodes = this.manager.getOrderedRefs();
519 | const deltaScroll = {
520 | left: this.scrollContainer.scrollLeft - this.initialScroll.left,
521 | top: this.scrollContainer.scrollTop - this.initialScroll.top,
522 | };
523 | const sortingOffset = {
524 | left: this.offsetEdge.left + this.translate.x + deltaScroll.left,
525 | top: this.offsetEdge.top + this.translate.y + deltaScroll.top,
526 | };
527 | const scrollDifference = {
528 | top: (window.pageYOffset - this.initialWindowScroll.top),
529 | left: (window.pageXOffset - this.initialWindowScroll.left),
530 | };
531 | this.newIndex = null;
532 |
533 | for (let i = 0, len = nodes.length; i < len; i++) {
534 | const {node} = nodes[i];
535 | const index = node.sortableInfo.index;
536 | const width = node.offsetWidth;
537 | const height = node.offsetHeight;
538 | const offset = {
539 | width: this.width > width ? width / 2 : this.width / 2,
540 | height: this.height > height ? height / 2 : this.height / 2,
541 | };
542 |
543 | const translate = {
544 | x: 0,
545 | y: 0,
546 | };
547 | let {edgeOffset} = nodes[i];
548 |
549 | // If we haven't cached the node's offsetTop / offsetLeft value
550 | if (!edgeOffset) {
551 | nodes[i].edgeOffset = (edgeOffset = this.getEdgeOffset(node));
552 | }
553 |
554 | // Get a reference to the next and previous node
555 | const nextNode = i < nodes.length - 1 && nodes[i + 1];
556 | const prevNode = i > 0 && nodes[i - 1];
557 |
558 | // Also cache the next node's edge offset if needed.
559 | // We need this for calculating the animation in a grid setup
560 | if (nextNode && !nextNode.edgeOffset) {
561 | nextNode.edgeOffset = this.getEdgeOffset(nextNode.node);
562 | }
563 |
564 | // If the node is the one we're currently animating, skip it
565 | if (index === this.index) {
566 | if (hideSortableGhost) {
567 | /*
568 | * With windowing libraries such as `react-virtualized`, the sortableGhost
569 | * node may change while scrolling down and then back up (or vice-versa),
570 | * so we need to update the reference to the new node just to be safe.
571 | */
572 | this.sortableGhost = node;
573 | // yyl
574 | node.style.visibility = 'hidden';
575 | node.style.visibility = 'hidden';
576 | // node.style.background = 'red';
577 | }
578 | continue;
579 | }
580 |
581 | if (transitionDuration) {
582 | node.style[
583 | `${vendorPrefix}TransitionDuration`
584 | ] = `${transitionDuration}ms`;
585 | }
586 |
587 | if (this._axis.x) {
588 | if (this._axis.y) {
589 | // Calculations for a grid setup
590 | if (
591 | index < this.index &&
592 | (
593 | ((sortingOffset.left + scrollDifference.left) - offset.width <= edgeOffset.left &&
594 | (sortingOffset.top + scrollDifference.top) <= edgeOffset.top + offset.height) ||
595 | (sortingOffset.top + scrollDifference.top) + offset.height <= edgeOffset.top
596 | )
597 | ) {
598 | // If the current node is to the left on the same row, or above the node that's being dragged
599 | // then move it to the right
600 | translate.x = this.width + this.marginOffset.x;
601 | if (
602 | edgeOffset.left + translate.x >
603 | this.containerBoundingRect.width - offset.width
604 | ) {
605 | // If it moves passed the right bounds, then animate it to the first position of the next row.
606 | // We just use the offset of the next node to calculate where to move, because that node's original position
607 | // is exactly where we want to go
608 | translate.x = nextNode.edgeOffset.left - edgeOffset.left;
609 | translate.y = nextNode.edgeOffset.top - edgeOffset.top;
610 | }
611 | if (this.newIndex === null) {
612 | this.newIndex = index;
613 | }
614 | } else if (
615 | index > this.index &&
616 | (
617 | ((sortingOffset.left + scrollDifference.left) + offset.width >= edgeOffset.left &&
618 | (sortingOffset.top + scrollDifference.top) + offset.height >= edgeOffset.top) ||
619 | (sortingOffset.top + scrollDifference.top) + offset.height >= edgeOffset.top + height
620 | )
621 | ) {
622 | // If the current node is to the right on the same row, or below the node that's being dragged
623 | // then move it to the left
624 | translate.x = -(this.width + this.marginOffset.x);
625 | if (
626 | edgeOffset.left + translate.x <
627 | this.containerBoundingRect.left + offset.width
628 | ) {
629 | // If it moves passed the left bounds, then animate it to the last position of the previous row.
630 | // We just use the offset of the previous node to calculate where to move, because that node's original position
631 | // is exactly where we want to go
632 | translate.x = prevNode.edgeOffset.left - edgeOffset.left;
633 | translate.y = prevNode.edgeOffset.top - edgeOffset.top;
634 | }
635 | this.newIndex = index;
636 | }
637 | } else {
638 | if (
639 | index > this.index &&
640 | (sortingOffset.left + scrollDifference.left) + offset.width >= edgeOffset.left
641 | ) {
642 | translate.x = -(this.width + this.marginOffset.x);
643 | this.newIndex = index;
644 | } else if (
645 | index < this.index &&
646 | (sortingOffset.left + scrollDifference.left) <= edgeOffset.left + offset.width
647 | ) {
648 | translate.x = this.width + this.marginOffset.x;
649 | if (this.newIndex == null) {
650 | this.newIndex = index;
651 | }
652 | }
653 | }
654 | } else if (this._axis.y) {
655 | if (
656 | index > this.index &&
657 | (sortingOffset.top + scrollDifference.top) + offset.height >= edgeOffset.top
658 | ) {
659 | translate.y = -(this.height + this.marginOffset.y);
660 | this.newIndex = index;
661 | } else if (
662 | index < this.index &&
663 | (sortingOffset.top + scrollDifference.top) <= edgeOffset.top + offset.height
664 | ) {
665 | translate.y = this.height + this.marginOffset.y;
666 | if (this.newIndex == null) {
667 | this.newIndex = index;
668 | }
669 | }
670 | }
671 | node.style[`${vendorPrefix}Transform`] = `translate3d(${translate.x}px,${translate.y}px,0)`;
672 | }
673 |
674 | if (this.newIndex == null) {
675 | this.newIndex = this.index;
676 | }
677 | },
678 |
679 | autoscroll() {
680 | const translate = this.translate;
681 | const direction = {
682 | x: 0,
683 | y: 0,
684 | };
685 | const speed = {
686 | x: 1,
687 | y: 1,
688 | };
689 | const acceleration = {
690 | x: 10,
691 | y: 10,
692 | };
693 |
694 | if (translate.y >= this.maxTranslate.y - this.height / 2) {
695 | direction.y = 1; // Scroll Down
696 | speed.y = acceleration.y * Math.abs((this.maxTranslate.y - this.height / 2 - translate.y) / this.height);
697 | } else if (translate.x >= this.maxTranslate.x - this.width / 2) {
698 | direction.x = 1; // Scroll Right
699 | speed.x = acceleration.x * Math.abs((this.maxTranslate.x - this.width / 2 - translate.x) / this.width);
700 | } else if (translate.y <= this.minTranslate.y + this.height / 2) {
701 | direction.y = -1; // Scroll Up
702 | speed.y = acceleration.y * Math.abs((translate.y - this.height / 2 - this.minTranslate.y) / this.height);
703 | } else if (translate.x <= this.minTranslate.x + this.width / 2) {
704 | direction.x = -1; // Scroll Left
705 | speed.x = acceleration.x * Math.abs((translate.x - this.width / 2 - this.minTranslate.x) / this.width);
706 | }
707 |
708 | if (this.autoscrollInterval) {
709 | clearInterval(this.autoscrollInterval);
710 | this.autoscrollInterval = null;
711 | this.isAutoScrolling = false;
712 | }
713 |
714 | if (direction.x !== 0 || direction.y !== 0) {
715 | this.autoscrollInterval = setInterval(
716 | () => {
717 | this.isAutoScrolling = true;
718 | const offset = {
719 | left: 1 * speed.x * direction.x,
720 | top: 1 * speed.y * direction.y,
721 | };
722 | this.scrollContainer.scrollTop += offset.top;
723 | this.scrollContainer.scrollLeft += offset.left;
724 | this.translate.x += offset.left;
725 | this.translate.y += offset.top;
726 | this.animateNodes();
727 | },
728 | 5
729 | );
730 | }
731 | },
732 | },
733 | };
734 |
--------------------------------------------------------------------------------