fix home characters slider pagination

fix home characters slider pagination

<template lang="jade">
	.Home_page
		ParallaxedLayersGroup
			VisMaVie_navigation_bar_layer

			ParallaxedLayer.Home_page-header-layer(:zIndex="5")
				ScreenFillerBlock.Home_page-header(:style="{backgroundImage: headerBackgroundUrl}", :percentageOfScreenFilled="configScreenFiller")
					.Home_page-header-useful-width
						.Home_page-header-margin-constraints
							.Home_page-header-content
								.Home_page-header-logo
									.Home_page-header-logo-offset
									LogoXebiaVisMaVie
								.Home_page-header-characters-scroll-view-pagination
									button.Home_page-header-characters-scroll-view-pagination-link(v-for="(c, i) in characters", @click="buttonClick($event, 'paginationLink'+i, scrollToSlideClick, i)", :ref="'paginationLink'+i", rel="button", :class="i === currentSlide ? 'is--active' : ''")
								.Home_page-header-characters-scroll-view(:style="{bottom:scrollViewBottomStyle}", ref="characterScrollView")
									.Home_page-header-characters
										.Home_page-header-characters-group(v-for="(charactersGroup, i) in charactersGroups")
											a.Home_page-header-character(v-for="(character, index) in charactersGroup", :class="'index-'+(index+1+(i*4))", :href="character.url", @click="clickOnCharacter($event, character)")
												.Home_page-header-character-image(:style="charactersStyles[index+(i*4)]")
												.Home_page-header-character-widget
													.Home_page-header-character-widget-outer-wrapper
														.Home_page-header-character-widget-inner-wrapper
															.Home_page-header-character-info
																.Home_page-header-character-name {{character.name}}
																.Home_page-header-character-job {{character.job}}
															.Home_page-header-character-link
																.Home_page-header-character-link-label {{header.consultants_evolution_label}}
																.Home_page-header-character-link-label-picto
																	ArrowBottom

			CallToActionLayer(ref="callToActionLayer", :zIndex="6", style="position:relative")

			ParallaxedLayer
				AppSection.AppSection-odd(:title="timelineItems.introductionTitle", :baseline="timelineItems.baseline")
					ContentBlock.Home_page-introduction(:blocks="timelineIntroduction.block_content", slot="section-content")

				AppSection.AppSection-odd(:title="timelineItems.name")
					Timeline(:items="timelineItems.data", slot="section-content").Home_page-timeline

			VisMaVie_footer_layer
</template>

<script>
	import getScrollBarWidth from 'tools/get-scroll-bar-width'
	const scrollBarWidth = getScrollBarWidth();

	import {domHeight, domWidth} from '@alexistessier/dom';

	import scrollBehaviorScrollToCareerObject from 'tools/scroll-behavior-scroll-to-career-object';

	import ParallaxedLayersGroup from 'xebia-web-common/components/ParallaxedLayersGroup/ParallaxedLayersGroup';
	import ParallaxedLayer from 'xebia-web-common/components/ParallaxedLayersGroup/ParallaxedLayer';

	import ScreenFillerBlock from 'xebia-web-common/components/ScreenFillerBlock';
	import LogoXebiaVisMaVie from 'generated/assets/components/Home_page/LogoXebiaVisMaVie';
	import ArrowBottom from 'generated/assets/components/Home_page/ArrowBottom';
	import CallToActionLayer from 'components/CallToActionLayer';

    import VisMaVie_navigation_bar_layer from 'components/VisMaVie_navigation_bar_layer';
    import VisMaVie_footer_layer from 'components/VisMaVie_footer_layer'

	import header from 'data/home/header.json';

	import {mixin as sizeClassHelper} from 'tools/size-class-helper';
	import {mixin as fontLoader} from 'tools/font-loader';

	import {mixin as uiNavigationButton} from 'xebia-web-common/tools/ui-navigation-button';

	import AppSection from 'components/AppSection';
	import Timeline from 'components/Timeline';
	import timelineItems from 'data/about/timeline-items.json';
	import timelineIntroduction from 'data/about/timeline-introduction.yaml';

	console.log(timelineIntroduction.block_content)

	import ContentBlock from 'components/ContentBlock'

	export default {
		name: 'Home_page',
		mixins: [sizeClassHelper, fontLoader, uiNavigationButton],
		data(){
			return {
				header,
				characters: header.consultants,
				headerBackground: undefined,
				charactersBackgrounds: [],
				scrollViewBottomStyle: `-${scrollBarWidth}px`,
				currentSlide: 0,
				slideWidth: 0,
				needAdjust: false,
				timelineItems,
				timelineIntroduction
			}
		},
		watch: {
			slideWidth(){
				this.needAdjust = true;
			}
		},
		computed: {
			charactersGroups: function () {
				return [
					[this.characters[0], this.characters[1], this.characters[2], this.characters[3]],
					[this.characters[4], this.characters[5], this.characters[6], this.characters[7]]
				];
			},
			charactersStyles(){
				return this.characters.map((character, i) => ({
					backgroundImage: this.charactersBackgrounds[i] ? `url("${this.charactersBackgrounds[i]}")` : "none"
				}))
			},
			headerBackgroundUrl(){
				return this.headerBackground ? `url('${this.headerBackground}')` : undefined;
			},
			configScreenFiller(){
				const self = this;

				function fillerHeight(height){
					const layer = self.$refs.callToActionLayer;
					return (layer && layer.$el ? height - domHeight(layer.$el) : height);
				}

				return function(height){
					return Math.max(fillerHeight(height), 425)+'px';
				}
			}
		},
		components: {
			ParallaxedLayersGroup,
			ParallaxedLayer,
			VisMaVie_navigation_bar_layer,
			VisMaVie_footer_layer,
			ScreenFillerBlock,
			LogoXebiaVisMaVie,
			CallToActionLayer,
			ArrowBottom,
			AppSection,
			Timeline,
			ContentBlock
		},
		methods: {
			clickOnCharacter(event, character){
				event.preventDefault();

				scrollBehaviorScrollToCareerObject.value = true;
				this.$router.push(character.url);
			},
			updateLayoutOnChange(){
				const sizeClassHelper = this.getSizeClassHelper();

				if (sizeClassHelper.isActive('width-compact')) {
					require.ensure([], require => {
						this.headerBackground = require('generated/assets/components/Home_page/images-blur/'+header.image.mobile);
					});
				}
				else{
					require.ensure([], require => {
						this.headerBackground = require('generated/assets/components/Home_page/images-blur/'+header.image.default)
					});
				}

				this.characters.forEach((character, i) => {
					require.ensure([], require => {
						this.charactersBackgrounds[i] = require('generated/assets/components/Home_page/images/'+character.image);
						this.charactersBackgrounds = this.charactersBackgrounds.map(s => s);
					});
				});
			},
			updateLayoutOnResize(){
				const scrollView = this.$refs.characterScrollView;

				this.slideWidth = scrollView ? domWidth(scrollView) : 0;
			},
			scrollToSlide(slide){
				const scrollView = this.$refs.characterScrollView;

				if (scrollView) {
					const slideWidth = domWidth(scrollView);

					scrollView.scrollTo({
	                    behavior: 'smooth',
	                    left: slide*slideWidth,
	                    top: 0
	                });
				}
			},
			scrollToSlideClick(event, ref, s, slide){
				this.scrollToSlide(slide);
			}
		},
		created() {
			this.loadFont({
				text: ['regular'],
				title: ['bold', 'regular']
			});

			const sizeClassHelper = this.getSizeClassHelper();

			const change = () => {
				this.updateLayoutOnChange();
			}

			this.deviceChangeListenerArguments = ['change', change];

			sizeClassHelper.on(...this.deviceChangeListenerArguments);

			const resize = () => {
				this.updateLayoutOnResize();
			}

			this.resizeListenerArguments = ['resize', resize];

			sizeClassHelper.on(...this.resizeListenerArguments);

			this.stopLoop = false;
			const self = this;

			let timeSinceNoPositionChange = 0;
			let previousPosition;
			let previousTime = Date.now();
			let adjusted = false;

			(function loop(){
				const currentTime = Date.now();
				const deltaTime = currentTime - previousTime;
				previousTime = currentTime;

				requestAnimationFrame(()=>{
					if (sizeClassHelper.isActive('width-compact') || Modernizr.touchevents) {
						const scrollView = self.$refs.characterScrollView;
						if (scrollView) {
							const slideWidth = self.slideWidth;
							const fullWidth = slideWidth*8;
							const position = scrollView.scrollLeft;

							const currentSlide = (slideWidth ? parseInt((position+slideWidth/2)/slideWidth) : 0);

							self.currentSlide = currentSlide;

							if (position === previousPosition) {
								timeSinceNoPositionChange += deltaTime;
							}
							else{
								timeSinceNoPositionChange = 0;
								adjusted = false;
							}

							if ((timeSinceNoPositionChange >= 120 && !adjusted) || self.needAdjust) {
								adjusted = true;
								self.needAdjust = false;
								self.scrollToSlide(self.currentSlide);
							}

							previousPosition = position;
						}
					}

					if (!self.stopLoop) {
						loop();
					}
				})
			})();
		},
		beforeDestroy(){
			this.stopLoop = true;
			this.getSizeClassHelper().off(...this.deviceChangeListenerArguments);
			this.getSizeClassHelper().off(...this.resizeListenerArguments);
		}
	}
</script>

<style lang="stylus">
	.Home_page-header-useful-width
		layout__outerBox()
		height 100%
	.Home_page-header-margin-constraints
		layout__innerBox()
		height 100%

	.Home_page-header
		background-repeat no-repeat
		background-size 100% 100%
		background-size cover
		background-position center center

	.Home_page-header-content
		height 100%
		position relative

		.size-class-not-width-compact.no-touchevents &
			layout__centeredGridBox(28)

	.Home_page-header-logo
		height 100%

		.size-class-not-width-compact.no-touchevents &
			layout__gridBox(8, gridNumberOfColumns: 28)

		.size-class-width-compact &,
		.touchevents &
			layout__gridBox(8, gridNumberOfColumns: 30)

		> .LogoXebiaVisMaVie
			width 100%
			height (105 / 570 * 100%)
			max-height 105px
			max-width 260px
			min-width 240px
			min-height 100px

			filter__dropShadow 0px 0px 2px rgba(black, 0.62)

	.Home_page-header-logo-offset
		width 100%
		height (130 / 570 * 100%)

		.size-class-width-compact &,
		.touchevents &
			height 80px

	.Home_page-header-characters-scroll-view
		width 100%
		width calc(100% + 40px)
		height 100%
		position absolute
		bottom 0
		left -20px

		.size-class-width-compact &,
		.touchevents &
			overflow-y hidden
			overflow-x scroll
			pointer-events auto
			-webkit-overflow-scrolling: touch;

		.size-class-not-width-compact.no-touchevents &
			height (250 / 570 * 100%)
			width 100%
			left 0

	.Home_page-header-characters
		clearfix()
		width 800%
		position absolute
		bottom 0
		left 0
		height 55%

		.size-class-not-width-compact.no-touchevents &
			width 100%
			height (250 / 570 * 100%)

	.Home_page-header-characters-group
		height 100%
		float left
		clearfix()
		position relative
		z-index 5
		width 50%

		.size-class-not-width-compact.no-touchevents &
			left -1.1vw

		&:hover
			z-index 6

	.Home_page-header-character
		display block
		height 100%
		width (100% / 4)
		float left
		position relative

		&:hover
			cursor pointer

		.size-class-not-width-compact.no-touchevents &.index-1
			left -1vw

		&.index-1, &.index-8
			z-index 8
		&.index-2, &.index-7
			z-index 6
		&.index-3, &.index-6
			z-index 4
		&.index-4, &.index-5
			z-index 2

			&:hover, &:focus
				z-index 3

		.size-class-not-width-compact.no-touchevents &.index-4
			left -0.5vw

	.Home_page-header-character-image
		position absolute
		bottom 0
		background-size 160px auto
		background-repeat no-repeat
		background-position 10px bottom
		z-index 5
		height 120%
		width 100%

		.size-class-not-width-compact.no-touchevents &
			height 270px
			width 160px
			height 20vw
			max-height 300px
			width 15vw
			background-size auto 100%
			background-position center bottom

	.Home_page-header-character-widget
		width 67%
		position absolute
		z-index 2
		bottom 40px
		left 90px

		.size-class-not-width-compact.no-touchevents &
			left 75%
			width 25%
			bottom 30px

	.Home_page-header-character-widget-outer-wrapper
		position relative
		overflow hidden
		pointer-events none
		width 100%

		.size-class-not-width-compact.no-touchevents &
			width 250px
			width calc(250px + 4vw)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			left -300px

	.Home_page-header-character-widget-inner-wrapper
		transition transform 220ms ease__outQuart()

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			transform translateX(-101%)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			transform translateX(101%)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character:hover &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character:focus &
			transform translateX(0)

	.Home_page-header-character-info
		background-color color__$blue
		padding-right 15px

	_characterWidgetMarginMacro()
		pxValue = 80px
		vwValue = 6.5vw

		margin-left 90px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			margin-left pxValue
			margin-left vwValue
			text-align left

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			margin-right pxValue * 1.3
			margin-right vwValue * 1.3
			margin-left 0
			text-align right

	.Home_page-header-character-name,
	.Home_page-header-character-job
		_characterWidgetMarginMacro()
		color white

	.Home_page-header-character-link
		display block
		background-color color__$flashOrange
		color white
		text-decoration none
		text-transform uppercase
		font__useTextRegular 12
		pointer-events auto
		font__line-height 15

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			clearfix()

	.Home_page-header-character-link-label
		_characterWidgetMarginMacro()
		display block
		width 120px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			float right

	.Home_page-header-character-info,
	.Home_page-header-character-link
		margin-top 5px
		padding-top 8px
		padding-bottom 8px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			padding-right 12px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			padding-left 12px

	.Home_page-header-character-name
		font__useTitleBold 21
		font__line-height 23

	.Home_page-header-character-name+.Home_page-header-character-job
		margin-top 5px

	.Home_page-header-character-job
		font__useTextRegular 14
		font__line-height 16

	.Home_page-header-character-link
		position relative

	.Home_page-header-character-link-label-picto
		width 15px
		height 15px
		position absolute
		top 15px
		transform rotate(-90deg)
		right 6px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			transform rotate(-90deg)
			right 15px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			transform rotate(90deg)
			left 15px

	.Home_page-header-characters-scroll-view-pagination
		height 12px
		clearfix()
		position absolute
		bottom 10px
		right 0
		z-index 8

		.size-class-not-width-compact.no-touchevents &
			display none

	.Home_page-header-characters-scroll-view-pagination-link
		button__resetStyle()
		float left
		background-color transparent
		shape__circle(8px)
		border 1px solid white
		pointer-events auto

		&:hover, &:focus
			cursor pointer
			background-color rgba(white, 0.5)

		&.is--active
			background-color white

	.Home_page-header-characters-scroll-view-pagination-link+.Home_page-header-characters-scroll-view-pagination-link
		margin-left 8px

	.Home_page-timeline
		margin-top 30px
</style>

<template lang="jade">
	.Home_page
		ParallaxedLayersGroup
			VisMaVie_navigation_bar_layer

			ParallaxedLayer.Home_page-header-layer(:zIndex="5")
				ScreenFillerBlock.Home_page-header(:style="{backgroundImage: headerBackgroundUrl}", :percentageOfScreenFilled="configScreenFiller")
					.Home_page-header-useful-width
						.Home_page-header-margin-constraints
							.Home_page-header-content
								.Home_page-header-logo
									.Home_page-header-logo-offset
									LogoXebiaVisMaVie
								.Home_page-header-characters-scroll-view-pagination
									button.Home_page-header-characters-scroll-view-pagination-link(v-for="(c, i) in characters", @click="buttonClick($event, 'paginationLink'+i, scrollToSlideClick, i)", :ref="'paginationLink'+i", rel="button", :class="i === currentSlide ? 'is--active' : ''")
								.Home_page-header-characters-scroll-view(:style="{bottom:scrollViewBottomStyle}", ref="characterScrollView")
									.Home_page-header-characters
										.Home_page-header-characters-group(v-for="(charactersGroup, i) in charactersGroups")
											a.Home_page-header-character(v-for="(character, index) in charactersGroup", :class="'index-'+(index+1+(i*4))", :href="character.url", @click="clickOnCharacter($event, character)")
												.Home_page-header-character-image(:style="charactersStyles[index+(i*4)]")
												.Home_page-header-character-widget
													.Home_page-header-character-widget-outer-wrapper
														.Home_page-header-character-widget-inner-wrapper
															.Home_page-header-character-info
																.Home_page-header-character-name {{character.name}}
																.Home_page-header-character-job {{character.job}}
															.Home_page-header-character-link
																.Home_page-header-character-link-label {{header.consultants_evolution_label}}
																.Home_page-header-character-link-label-picto
																	ArrowBottom

			CallToActionLayer(ref="callToActionLayer", :zIndex="6", style="position:relative")

			ParallaxedLayer
				AppSection.AppSection-odd(:title="timelineItems.introductionTitle", :baseline="timelineItems.baseline")
					ContentBlock.Home_page-introduction(:blocks="timelineIntroduction.block_content", slot="section-content")

				AppSection.AppSection-odd(:title="timelineItems.name")
					Timeline(:items="timelineItems.data", slot="section-content").Home_page-timeline

			VisMaVie_footer_layer
</template>

<script>
	import getScrollBarWidth from 'tools/get-scroll-bar-width'
	const scrollBarWidth = getScrollBarWidth();

	import {domHeight, domWidth} from '@alexistessier/dom';

	import scrollBehaviorScrollToCareerObject from 'tools/scroll-behavior-scroll-to-career-object';

	import ParallaxedLayersGroup from 'xebia-web-common/components/ParallaxedLayersGroup/ParallaxedLayersGroup';
	import ParallaxedLayer from 'xebia-web-common/components/ParallaxedLayersGroup/ParallaxedLayer';

	import ScreenFillerBlock from 'xebia-web-common/components/ScreenFillerBlock';
	import LogoXebiaVisMaVie from 'generated/assets/components/Home_page/LogoXebiaVisMaVie';
	import ArrowBottom from 'generated/assets/components/Home_page/ArrowBottom';
	import CallToActionLayer from 'components/CallToActionLayer';

    import VisMaVie_navigation_bar_layer from 'components/VisMaVie_navigation_bar_layer';
    import VisMaVie_footer_layer from 'components/VisMaVie_footer_layer'

	import header from 'data/home/header.json';

	import {mixin as sizeClassHelper} from 'tools/size-class-helper';
	import {mixin as fontLoader} from 'tools/font-loader';

	import {mixin as uiNavigationButton} from 'xebia-web-common/tools/ui-navigation-button';

	import AppSection from 'components/AppSection';
	import Timeline from 'components/Timeline';
	import timelineItems from 'data/about/timeline-items.json';
	import timelineIntroduction from 'data/about/timeline-introduction.yaml';

	console.log(timelineIntroduction.block_content)

	import ContentBlock from 'components/ContentBlock'

	export default {
		name: 'Home_page',
		mixins: [sizeClassHelper, fontLoader, uiNavigationButton],
		data(){
			return {
				header,
				characters: header.consultants,
				headerBackground: undefined,
				charactersBackgrounds: [],
				scrollViewBottomStyle: `-${scrollBarWidth}px`,
				currentSlide: 0,
				slideWidth: 0,
				needAdjust: false,
				timelineItems,
				timelineIntroduction
			}
		},
		watch: {
			slideWidth(){
				this.needAdjust = true;
			}
		},
		computed: {
			charactersGroups: function () {
				return [
					[this.characters[0], this.characters[1], this.characters[2], this.characters[3]],
					[this.characters[4], this.characters[5], this.characters[6], this.characters[7]]
				];
			},
			charactersStyles(){
				return this.characters.map((character, i) => ({
					backgroundImage: this.charactersBackgrounds[i] ? `url("${this.charactersBackgrounds[i]}")` : "none"
				}))
			},
			headerBackgroundUrl(){
				return this.headerBackground ? `url('${this.headerBackground}')` : undefined;
			},
			configScreenFiller(){
				const self = this;

				function fillerHeight(height){
					const layer = self.$refs.callToActionLayer;
					return (layer && layer.$el ? height - domHeight(layer.$el) : height);
				}

				return function(height){
					return Math.max(fillerHeight(height), 425)+'px';
				}
			}
		},
		components: {
			ParallaxedLayersGroup,
			ParallaxedLayer,
			VisMaVie_navigation_bar_layer,
			VisMaVie_footer_layer,
			ScreenFillerBlock,
			LogoXebiaVisMaVie,
			CallToActionLayer,
			ArrowBottom,
			AppSection,
			Timeline,
			ContentBlock
		},
		methods: {
			clickOnCharacter(event, character){
				event.preventDefault();

				scrollBehaviorScrollToCareerObject.value = true;
				this.$router.push(character.url);
			},
			updateLayoutOnChange(){
				const sizeClassHelper = this.getSizeClassHelper();

				if (sizeClassHelper.isActive('width-compact')) {
					require.ensure([], require => {
						this.headerBackground = require('generated/assets/components/Home_page/images-blur/'+header.image.mobile);
					});
				}
				else{
					require.ensure([], require => {
						this.headerBackground = require('generated/assets/components/Home_page/images-blur/'+header.image.default)
					});
				}

				this.characters.forEach((character, i) => {
					require.ensure([], require => {
						this.charactersBackgrounds[i] = require('generated/assets/components/Home_page/images/'+character.image);
						this.charactersBackgrounds = this.charactersBackgrounds.map(s => s);
					});
				});
			},
			updateLayoutOnResize(){
				const scrollView = this.$refs.characterScrollView;

				this.slideWidth = scrollView ? domWidth(scrollView) : 0;
			},
			scrollToSlide(slide){
				const scrollView = this.$refs.characterScrollView;

				if (scrollView) {
					const slideWidth = domWidth(scrollView);

					scrollView.scrollTo({
	                    behavior: 'smooth',
	                    left: slide*slideWidth,
	                    top: 0
	                });
				}
			},
			scrollToSlideClick(event, ref, s, slide){
				this.scrollToSlide(slide);
			}
		},
		created() {
			this.loadFont({
				text: ['regular'],
				title: ['bold', 'regular']
			});

			const sizeClassHelper = this.getSizeClassHelper();

			const change = () => {
				this.updateLayoutOnChange();
			}

			this.deviceChangeListenerArguments = ['change', change];

			sizeClassHelper.on(...this.deviceChangeListenerArguments);

			const resize = () => {
				this.updateLayoutOnResize();
			}

			this.resizeListenerArguments = ['resize', resize];

			sizeClassHelper.on(...this.resizeListenerArguments);

			this.stopLoop = false;
			const self = this;

			let timeSinceNoPositionChange = 0;
			let previousPosition;
			let previousTime = Date.now();
			let adjusted = false;

			(function loop(){
				const currentTime = Date.now();
				const deltaTime = currentTime - previousTime;
				previousTime = currentTime;

				requestAnimationFrame(()=>{
					if (sizeClassHelper.isActive('width-compact') || Modernizr.touchevents) {
						const scrollView = self.$refs.characterScrollView;
						if (scrollView) {
							const slideWidth = self.slideWidth;
							const fullWidth = slideWidth*8;
							const position = scrollView.scrollLeft;

							const currentSlide = (slideWidth ? parseInt((position+slideWidth/2)/slideWidth) : 0);

							self.currentSlide = currentSlide;

							if (position === previousPosition) {
								timeSinceNoPositionChange += deltaTime;
							}
							else{
								timeSinceNoPositionChange = 0;
								adjusted = false;
							}

							if ((timeSinceNoPositionChange >= 120 && !adjusted) || self.needAdjust) {
								adjusted = true;
								self.needAdjust = false;
								self.scrollToSlide(self.currentSlide);
							}

							previousPosition = position;
						}
					}

					if (!self.stopLoop) {
						loop();
					}
				})
			})();
		},
		beforeDestroy(){
			this.stopLoop = true;
			this.getSizeClassHelper().off(...this.deviceChangeListenerArguments);
			this.getSizeClassHelper().off(...this.resizeListenerArguments);
		}
	}
</script>

<style lang="stylus">
	.Home_page-header-useful-width
		layout__outerBox()
		height 100%
	.Home_page-header-margin-constraints
		layout__innerBox()
		height 100%

	.Home_page-header
		background-repeat no-repeat
		background-size 100% 100%
		background-size cover
		background-position center center

	.Home_page-header-content
		height 100%
		position relative

		.size-class-not-width-compact.no-touchevents &
			layout__centeredGridBox(28)

	.Home_page-header-logo
		height 100%

		.size-class-not-width-compact.no-touchevents &
			layout__gridBox(8, gridNumberOfColumns: 28)

		.size-class-width-compact &,
		.touchevents &
			layout__gridBox(8, gridNumberOfColumns: 30)

		> .LogoXebiaVisMaVie
			width 100%
			height (105 / 570 * 100%)
			max-height 105px
			max-width 260px
			min-width 240px
			min-height 100px

			filter__dropShadow 0px 0px 2px rgba(black, 0.62)

	.Home_page-header-logo-offset
		width 100%
		height (130 / 570 * 100%)

		.size-class-width-compact &,
		.touchevents &
			height 80px

	.Home_page-header-characters-scroll-view
		width 100%
		width calc(100% + 40px)
		height 100%
		position absolute
		bottom 0
		left -20px

		.size-class-width-compact &,
		.touchevents &
			overflow-y hidden
			overflow-x scroll
			pointer-events auto
			-webkit-overflow-scrolling: touch;

		.size-class-not-width-compact.no-touchevents &
			height (250 / 570 * 100%)
			width 100%
			left 0

	.Home_page-header-characters
		clearfix()
		width 800%
		position absolute
		bottom 0
		left 0
		height 55%

		.size-class-not-width-compact.no-touchevents &
			width 100%
			height (250 / 570 * 100%)

	.Home_page-header-characters-group
		height 100%
		float left
		clearfix()
		position relative
		z-index 5
		width 50%

		.size-class-not-width-compact.no-touchevents &
			left -1.1vw

		&:hover
			z-index 6

	.Home_page-header-character
		display block
		height 100%
		width (100% / 4)
		float left
		position relative

		&:hover
			cursor pointer

		.size-class-not-width-compact.no-touchevents &.index-1
			left -1vw

		&.index-1, &.index-8
			z-index 8
		&.index-2, &.index-7
			z-index 6
		&.index-3, &.index-6
			z-index 4
		&.index-4, &.index-5
			z-index 2

			&:hover, &:focus
				z-index 3

		.size-class-not-width-compact.no-touchevents &.index-4
			left -0.5vw

	.Home_page-header-character-image
		position absolute
		bottom 0
		background-size 160px auto
		background-repeat no-repeat
		background-position 10px bottom
		z-index 5
		height 120%
		width 100%

		.size-class-not-width-compact.no-touchevents &
			height 270px
			width 160px
			height 20vw
			max-height 300px
			width 15vw
			background-size auto 100%
			background-position center bottom

	.Home_page-header-character-widget
		width 67%
		position absolute
		z-index 2
		bottom 40px
		left 90px

		.size-class-not-width-compact.no-touchevents &
			left 75%
			width 25%
			bottom 30px

	.Home_page-header-character-widget-outer-wrapper
		position relative
		overflow hidden
		pointer-events none
		width 100%

		.size-class-not-width-compact.no-touchevents &
			width 250px
			width calc(250px + 4vw)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			left -300px

	.Home_page-header-character-widget-inner-wrapper
		transition transform 220ms ease__outQuart()

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			transform translateX(-101%)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			transform translateX(101%)

		.size-class-not-width-compact.no-touchevents .Home_page-header-character:hover &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character:focus &
			transform translateX(0)

	.Home_page-header-character-info
		background-color color__$blue
		padding-right 15px

	_characterWidgetMarginMacro()
		pxValue = 80px
		vwValue = 6.5vw

		margin-left 90px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			margin-left pxValue
			margin-left vwValue
			text-align left

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			margin-right pxValue * 1.3
			margin-right vwValue * 1.3
			margin-left 0
			text-align right

	.Home_page-header-character-name,
	.Home_page-header-character-job
		_characterWidgetMarginMacro()
		color white

	.Home_page-header-character-link
		display block
		background-color color__$flashOrange
		color white
		text-decoration none
		text-transform uppercase
		font__useTextRegular 12
		pointer-events auto
		font__line-height 15

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			clearfix()

	.Home_page-header-character-link-label
		_characterWidgetMarginMacro()
		display block
		width 120px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			float right

	.Home_page-header-character-info,
	.Home_page-header-character-link
		margin-top 5px
		padding-top 8px
		padding-bottom 8px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			padding-right 12px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			padding-left 12px

	.Home_page-header-character-name
		font__useTitleBold 21
		font__line-height 23

	.Home_page-header-character-name+.Home_page-header-character-job
		margin-top 5px

	.Home_page-header-character-job
		font__useTextRegular 14
		font__line-height 16

	.Home_page-header-character-link
		position relative

	.Home_page-header-character-link-label-picto
		width 15px
		height 15px
		position absolute
		top 15px
		transform rotate(-90deg)
		right 6px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-1 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-2 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-3 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-4 &
			transform rotate(-90deg)
			right 15px

		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-5 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-6 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-7 &,
		.size-class-not-width-compact.no-touchevents .Home_page-header-character.index-8 &
			transform rotate(90deg)
			left 15px

	.Home_page-header-characters-scroll-view-pagination
		height 12px
		clearfix()
		position absolute
		bottom 10px
		right 0
		z-index 10

		.size-class-not-width-compact.no-touchevents &
			display none

	.Home_page-header-characters-scroll-view-pagination-link
		button__resetStyle()
		float left
		background-color transparent
		shape__circle(8px)
		border 1px solid white
		pointer-events auto

		&:hover, &:focus
			cursor pointer
			background-color rgba(white, 0.5)

		&.is--active
			background-color white

	.Home_page-header-characters-scroll-view-pagination-link+.Home_page-header-characters-scroll-view-pagination-link
		margin-left 8px

	.Home_page-timeline
		margin-top 30px
</style>


mobile z-index

mobile z-index


handle dropdown title

handle dropdown title

<template>
    <b-dropdown
        class="SharpDropdown"
        :toggle-class="toggleClass"
        :disabled="disabled"
        :no-caret="!showCaret"
        :offset="1"
        variant="custom"
        v-bind="$attrs"
        v-on="$listeners"
        ref="dropdown"
    >
        <template v-slot:button-content>
            <span class="d-inline-block">
                <slot name="text">{{ text }}</slot>
            </span>
        </template>

        <slot :hide="hide" />
    </b-dropdown>
</template>

<script>
    import { BDropdown } from 'bootstrap-vue';
    import Button from "../Button"

    export default {
        name: 'SharpDropdown',
        components: {
            BDropdown,
        },
        props: {
            ...Button.props,
            text: String,
            showCaret: {
                type: Boolean,
                default: true
            },
            title: String,
            disabled: Boolean,
        },
        computed: {
            ...Button.computed,
            /**
             * button variant is defined in classes
             */
            toggleClass() {
                return this.classes;
            },
        },
        methods: {
            hide() {
                this.$refs.dropdown.hide();
            },
        },
        mounted() {
            if(this.title) {
                this.$el.querySelector('.dropdown-toggle').setAttribute('title', this.title);
            }
        },
    }
</script>

<template>
    <b-dropdown
        class="SharpDropdown"
        :toggle-class="toggleClass"
        :disabled="disabled"
        :no-caret="!showCaret"
        :offset="1"
        variant="custom"
        v-bind="$attrs"
        v-on="$listeners"
        ref="dropdown"
    >
        <template v-slot:button-content>
            <span class="d-inline-block">
                <slot name="text">{{ text }}</slot>
            </span>
        </template>

        <slot :hide="hide" />
    </b-dropdown>
</template>

<script>
    import { BDropdown } from 'bootstrap-vue';
    import Button from "../Button"

    export default {
        name: 'SharpDropdown',
        components: {
            BDropdown,
        },
        props: {
            ...Button.props,
            text: String,
            showCaret: {
                type: Boolean,
                default: true
            },
            disabled: Boolean,
        },
        computed: {
            ...Button.computed,
            /**
             * button variant is defined in classes
             */
            toggleClass() {
                return this.classes;
            },
        },
        methods: {
            hide() {
                this.$refs.dropdown.hide();
            },
        },
    }
</script>


vue-link-preload changes; minor performance improvements, label safe mangle names

<!--
  Based on vue-futurelink with customisation for simplicity and performance
  REF: https://github.com/SamKnows/vue-futurelink
-->

<template>
  <div class="dn" aria-hidden="true">
    <Component :is="load"/>
  </div>
</template>

<script>
import futurelink from 'futurelink';

export default {
  name: 'TheLinkPreload',
  data: () => ({
    load: null,
    options: {
      links: [],
    },
  }),
  mounted() {
    // don't run on server
    if (typeof window === 'undefined') return;

    // don't run on mobile
    if ('ontouchstart' in window) return;

    const loaded = [this.$route.path];

    this.options.future = (link) => {
      const href = link.getAttribute('href');

      if (loaded.includes(href)) return;

      loaded.push(href);

      const resolver = this.$router.resolve(href);
      const { matched } = resolver.resolved;
      this.load = matched[matched.length - 1].components.default;
    };

    futurelink(this.options);

    this.__init();
    this.$router.afterEach(this.__init);
  },
  methods: {
    __init() {
      // no need to use $nextTick since it doesn't need to be immediate
      setTimeout(() => {
        const links = document.querySelectorAll('a[href^="/"]');

        // fancy high performance way to convert nodeList into Array
        // eslint-disable-next-line no-plusplus
        for (let i = -1, l = links.length; ++i !== l; this.options.links[i] = links[i]);
      }, 0);
    },
  },
};
</script>

<!--
  Based on vue-futurelink with customisation for simplicity and performance
  REF: https://github.com/SamKnows/vue-futurelink
-->

<template>
  <div class="dn" aria-hidden="true">
    <Component :is="load"/>
  </div>
</template>

<script>
import futurelink from 'futurelink';

export default {
  name: 'TheLinkPreload',
  data: () => ({
    load: undefined,
    options: {
      links: [],
    },
  }),
  mounted() {
    // Don't run on the server
    if (typeof window === 'undefined') {
      return;
    }

    // Don't run on mobile
    if ('ontouchstart' in window) {
      return;
    }

    const loaded = [this.$route.path];

    this.options.future = (link) => {
      const href = link.getAttribute('href');

      if (loaded.includes(href)) {
        return;
      }

      loaded.push(href);

      // this.$emit('preload', href);

      const resolved = this.$router.resolve(href);
      const { matched } = resolved.resolved;
      this.load = matched[matched.length - 1].components.default;
    };

    futurelink(this.options);

    this.init();
    this.$router.afterEach(this.init);
  },
  methods: {
    init() {
      this.$nextTick(() => {
        const links = document.querySelectorAll('a[href^="/"');
        // Populate this.options.links {Array} from links {nodeList}
        for (let i = -1, l = links.length; ++i !== l; this.options.links[i] = links[i]); // eslint-disable-line no-plusplus
      });
    },
  },
};
</script>


fix(DataField): should not expand by default if too many values if custom

<template>

    <VTooltip
      :style="{ marginLeft: depth * 14 + 'px' }"
      :disabled="!field.meta"
      :class="{
        'force-toolbar': contextMenuOpen || editing,
      }"
      class="self"
      placement="left"
      offset="24"
      @click.native="onClick"
      @mouseenter.native="onContextMenuMouseEnter"
      @mouseleave.native="onContextMenuMouseLeave"
    >

        <input
          ref="keyInput"
          v-model="editedKey"
          class="edit-input key-input"
          :class="{ error: !keyValid }"
          @keydown.esc.capture.stop.prevent="cancelEdit()"
          @keydown.enter="submitEdit()"
        >

      {{ field.key }}:

        <input
          ref="editInput"
          v-model="editedValue"
          class="edit-input value-input"
          :class="{ error: !valueValid }"
          list="special-tokens"
          @keydown.esc.capture.stop.prevent="cancelEdit()"
          @keydown.enter="submitEdit()"
        >

          <VueIcon
            v-if="!editValid"
            v-tooltip="editErrorMessage"
            class="small-icon warning"
            icon="warning"
          />
          <template v-else>
            <VueButton
              v-tooltip="$t('DataField.edit.cancel.tooltip')"
              class="icon-button flat"
              icon-left="cancel"
              @click="cancelEdit()"
            />
            <VueButton
              v-tooltip="$t('DataField.edit.submit.tooltip')"
              class="icon-button flat"
              icon-left="save"
              @click="submitEdit()"
            />
          </template>

      <template v-else>

          <VueButton
            v-if="isValueEditable"
            v-tooltip="'Edit value'"
            class="edit-value icon-button flat"
            icon-left="edit"
            @click="openEdit()"
          />
          <template v-if="quickEdits">
            <VueButton
              v-for="(info, index) of quickEdits"
              :key="index"
              v-tooltip="info.title || 'Quick edit'"
              :class="info.class"
              :icon-left="info.icon"
              class="quick-edit icon-button flat"
              @click="quickEdit(info, $event)"
            />
          </template>
          <VueButton
            v-if="isSubfieldsEditable && !addingValue"
            v-tooltip="'Add new value'"
            class="add-value icon-button flat"
            icon-left="add_circle"
            @click="addNewValue()"
          />
          <VueButton
            v-if="removable"
            v-tooltip="'Remove value'"
            class="remove-field icon-button flat"
            icon-left="delete"
            @click="removeField()"
          />

          <VueDropdown
            :open.sync="contextMenuOpen"
          >
            <VueButton
              slot="trigger"
              icon-left="more_vert"
              class="icon-button flat"
            />

              <VueDropdownButton
                icon-left="flip_to_front"
                @click="copyToClipboard"
              >
                {{ $t('DataField.contextMenu.copyValue') }}
              </VueDropdownButton>

          </VueDropdown>

      </template>

      <template #popper>

            {{ key }}
            {{ val }}

      </template>
    </VTooltip>

      <data-field
        v-for="subField in formattedSubFields"
        :key="subField.key"
        :field="subField"
        :parent-field="field"
        :depth="depth + 1"
        :path="`${path}.${subField.key}`"
        :editable="isEditable"
        :removable="isSubfieldsEditable"
        :renamable="editable && valueType === 'plain-object'"
        :force-collapse="forceCollapse"
        :is-state-field="isStateField"
      />
      <VueButton
        v-if="subFieldCount > limit"
        v-tooltip="'Show more'"
        :style="{ marginLeft: depthMargin + 'px' }"
        icon-left="more_horiz"
        class="icon-button flat more"
        @click="showMoreSubfields()"
      />
      <data-field
        v-if="isSubfieldsEditable && addingValue"
        ref="newField"
        :field="newField"
        :depth="depth + 1"
        :path="`${path}.${newField.key}`"
        :renamable="valueType === 'plain-object'"
        :force-collapse="forceCollapse"
        editable
        removable
        :is-state-field="isStateField"
        @cancel-edit="addingValue = false"
        @submit-edit="addingValue = false"
      />

</template>


Signed-off-by: DawnCHL <chen_huanlu@163.com>

<template>

				title:
				{{item.apiName}}

				url:
				{{url}}

				method:
				{{item.method}}

				respond:
				{{item.respond}}

</template>

<template>

{{item.apiName}}

{{url}}

{{item.respond}}

</template>


feat: unicloud-db add get temp lookup support

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import messages from './i18n/index'

const { t } = initVueI18n(messages)

const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}
const loadMode = {
  auto: 'auto',
  onready: 'onready',
  manual: 'manual'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where',
  'groupby',
  'groupField',
  'distinct'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: [String, Array],
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String, Object],
      default: false
    },
    gettreepath: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    groupby: {
      type: String,
      default: ''
    },
    groupField: {
      type: String,
      default: ''
    },
    distinct: {
      type: [Boolean, String],
      default: false
    },
    pageIndistinct: {
      type: [Boolean, String],
      default: false
    },
    foreignKey: {
      type: String,
      default: ''
    },
    loadtime: {
      type: String,
      default: 'auto'
    },
    manual: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {},
      errorMessage: ''
    }
  },
  computed: {
    collectionArgs () {
      return Array.isArray(this.collection) ? this.collection : [this.collection]
    },
    isLookup () {
      return (Array.isArray(this.collection) && this.collection.length > 1) || (typeof this.collection === 'string' && this.collection.indexOf(',') > -1)
    },
    mainCollection () {
      if (typeof this.collection === 'string') {
        return this.collection.split(',')[0]
      }
      const mainQuery = JSON.parse(JSON.stringify(this.collection[0]))
      return mainQuery.$db[0].$param[0]
    }
  },
  created () {
    this._isEnded = false
    this.paginationInternal = {
      current: this.pageCurrent,
      size: this.pageSize,
      count: 0
    }

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      if (this.loadtime === loadMode.manual) {
        return
      }

      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts || []
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual && this.loadtime === loadMode.auto) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      let clear = false
      if (typeof args1 === 'object') {
        if (args1.clear) {
          if (this.pageData === pageMode.replace) {
            this.clear()
          } else {
            clear = args1.clear
          }
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback, clear)
    },
    loadMore () {
      if (this._isEnded || this.loading) {
        return
      }

      if (this.pageData === pageMode.add) {
        this.paginationInternal.current++
      }

      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      db.collection(this.mainCollection).add(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.add.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (!id || !id.length) {
        return
      }
      if (!needConfirm) {
        this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        return
      }
      uni.showModal({
        title: confirmTitle || t('uniCloud.component.remove.showModal.title'),
        content: confirmContent || t('uniCloud.component.remove.showModal.content'),
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        }
      })
    },
    update (id, value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      return db.collection(this.mainCollection).doc(id).update(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.update.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    getTemp (isTemp = true) {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(...this.collectionArgs)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.foreignKey) {
        db = db.foreignKey(this.foreignKey)
      }
      if (this.groupby) {
        db = db.groupBy(this.groupby)
      }
      if (this.groupField) {
        db = db.groupField(this.groupField)
      }
      if (this.distinct === true) {
        db = db.distinct()
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      const treeOptions = {
        limitLevel: this.limitlevel,
        startWith: this.startwith
      }
      if (this.gettree) {
        getOptions.getTree = treeOptions
      }
      if (this.gettreepath) {
        getOptions.getTreePath = treeOptions
      }
      db = db.skip(size * (current - 1)).limit(size)

      if (isTemp) {
        db = db.getTemp(getOptions)
        db.udb = this
      } else {
        db = db.get(getOptions)
      }

      return db
    },
    setResult (result) {
      if (result.code === 0) {
        this._execLoadDataSuccess(result)
      } else {
        this._execLoadDataFail(new Error(result.message))
      }
    },
    _execLoadData (callback, clear) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        this._execLoadDataSuccess(res.result, callback, clear)

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this._execLoadDataFail(err, callback)
      })
    },
    _execLoadDataSuccess (result, callback, clear) {
      const {
        data,
        count
      } = result
      this._isEnded = count !== undefined ? (this.paginationInternal.current * this.paginationInternal.size >= count) : (data.length < this.pageSize)
      this.hasMore = !this._isEnded

      const data2 = this.getone ? (data.length ? data[0] : undefined) : data

      if (this.getcount) {
        this.paginationInternal.count = count
      }

      callback && callback(data2, this._isEnded, this.paginationInternal)
      this._dispatchEvent(events.load, data2)

      if (this.getone || this.pageData === pageMode.replace) {
        this.dataList = data2
      } else {
        if (clear) {
          this.dataList = data2
        } else {
          this.dataList.push(...data2)
        }
      }
    },
    _execLoadDataFail (err, callback) {
      this.errorMessage = err
      callback && callback()
      this.$emit(events.error, err)
      if (process.env.NODE_ENV === 'development') {
        console.error(err)
      }
    },
    _getExec () {
      return this.getTemp(false)
    },
    _execRemove (id, action, success, fail, complete, needConfirm, needLoading, loadingTitle) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      if (needLoading) {
        uni.showLoading({
          mask: true,
          title: loadingTitle
        })
      }

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this.mainCollection).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded, this.paginationInternal)
      } else {
        this.$emit(type, data, this._isEnded, this.paginationInternal)
      }
    }
  }
}
</script>

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import messages from './i18n/index'

const { t } = initVueI18n(messages)

const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}
const loadMode = {
  auto: 'auto',
  onready: 'onready',
  manual: 'manual'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where',
  'groupby',
  'groupField',
  'distinct'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: String,
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String, Object],
      default: false
    },
    gettreepath: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    groupby: {
      type: String,
      default: ''
    },
    groupField: {
      type: String,
      default: ''
    },
    distinct: {
      type: [Boolean, String],
      default: false
    },
    pageIndistinct: {
      type: [Boolean, String],
      default: false
    },
    foreignKey: {
      type: String,
      default: ''
    },
    loadtime: {
      type: String,
      default: 'auto'
    },
    manual: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {},
      errorMessage: ''
    }
  },
  created () {
    this._isEnded = false
    this.paginationInternal = {
      current: this.pageCurrent,
      size: this.pageSize,
      count: 0
    }

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      if (this.loadtime === loadMode.manual) {
        return
      }

      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts || []
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual && this.loadtime === loadMode.auto) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      let clear = false
      if (typeof args1 === 'object') {
        if (args1.clear) {
          if (this.pageData === pageMode.replace) {
            this.clear()
          } else {
            clear = args1.clear
          }
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback, clear)
    },
    loadMore () {
      if (this._isEnded || this.loading) {
        return
      }

      if (this.pageData === pageMode.add) {
        this.paginationInternal.current++
      }

      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      db.collection(this._getCollection()).add(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.add.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (!id || !id.length) {
        return
      }
      if (!needConfirm) {
        this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        return
      }
      uni.showModal({
        title: confirmTitle || t('uniCloud.component.remove.showModal.title'),
        content: confirmContent || t('uniCloud.component.remove.showModal.content'),
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        }
      })
    },
    update (id, value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      return db.collection(this._getCollection()).doc(id).update(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.update.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    getTemp (isTemp = true) {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(this.collection)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.foreignKey) {
        db = db.foreignKey(this.foreignKey)
      }
      if (this.groupby) {
        db = db.groupBy(this.groupby)
      }
      if (this.groupField) {
        db = db.groupField(this.groupField)
      }
      if (this.distinct === true) {
        db = db.distinct()
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      const treeOptions = {
        limitLevel: this.limitlevel,
        startWith: this.startwith
      }
      if (this.gettree) {
        getOptions.getTree = treeOptions
      }
      if (this.gettreepath) {
        getOptions.getTreePath = treeOptions
      }
      db = db.skip(size * (current - 1)).limit(size)

      if (isTemp) {
        db = db.getTemp(getOptions)
        db.udb = this
      } else {
        db = db.get(getOptions)
      }

      return db
    },
    setResult (result) {
      if (result.code === 0) {
        this._execLoadDataSuccess(result)
      } else {
        this._execLoadDataFail(new Error(result.message))
      }
    },
    _execLoadData (callback, clear) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        this._execLoadDataSuccess(res.result, callback, clear)

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this._execLoadDataFail(err, callback)
      })
    },
    _execLoadDataSuccess (result, callback, clear) {
      const {
        data,
        count
      } = result
      this._isEnded = count !== undefined ? (this.paginationInternal.current * this.paginationInternal.size >= count) : (data.length < this.pageSize)
      this.hasMore = !this._isEnded

      const data2 = this.getone ? (data.length ? data[0] : undefined) : data

      if (this.getcount) {
        this.paginationInternal.count = count
      }

      callback && callback(data2, this._isEnded, this.paginationInternal)
      this._dispatchEvent(events.load, data2)

      if (this.getone || this.pageData === pageMode.replace) {
        this.dataList = data2
      } else {
        if (clear) {
          this.dataList = data2
        } else {
          this.dataList.push(...data2)
        }
      }
    },
    _execLoadDataFail (err, callback) {
      this.errorMessage = err
      callback && callback()
      this.$emit(events.error, err)
      if (process.env.NODE_ENV === 'development') {
        console.error(err)
      }
    },
    _getExec () {
      return this.getTemp(false)
    },
    _execRemove (id, action, success, fail, complete, needConfirm, needLoading, loadingTitle) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      if (needLoading) {
        uni.showLoading({
          mask: true,
          title: loadingTitle
        })
      }

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this._getCollection()).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    _getCollection () {
      const index = this.collection.indexOf(',')
      const collection = index > 0 ? this.collection.substring(0, index) : this.collection
      return collection
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded, this.paginationInternal)
      } else {
        this.$emit(type, data, this._isEnded, this.paginationInternal)
      }
    }
  }
}
</script>


update: <unicloud-db> 增加属性 groupby,group-field,distinct

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where',
  'groupby',
  'groupField',
  'distinct'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: String,
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    manual: {
      type: Boolean,
      default: false
    },
    groupby: {
      type: String,
      default: ''
    },
    groupField: {
      type: String,
      default: ''
    },
    distinct: {
      type: [Boolean, String],
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {
        current: this.pageCurrent,
        size: this.pageSize,
        count: 0
      },
      errorMessage: ''
    }
  },
  created () {
    this._isEnded = false

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      if (typeof args1 === 'object') {
        if (args1.clear) {
          this.clear()
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback)
    },
    loadMore () {
      if (this._isEnded) {
        return
      }
      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      toastTitle,
      success,
      fail,
      complete
    } = {}) {
      uni.showLoading()
      /* eslint-disable no-undef */
      const db = uniCloud.database()
      db.collection(this.collection).add(value).then((res) => {
        success && success(res)
        uni.showToast({
          title: toastTitle || '新增成功'
        })
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent
    } = {}) {
      if (!id || !id.length) {
        return
      }
      uni.showModal({
        title: confirmTitle || '提示',
        content: confirmContent || '是否删除该数据',
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete)
        }
      })
    },
    update (id, value, {
      toastTitle,
      success,
      fail,
      complete
    } = {}) {
      uni.showLoading()
      /* eslint-disable no-undef */
      const db = uniCloud.database()
      return db.collection(this.collection).doc(id).update(value).then((res) => {
        success && success(res)
        uni.showToast({
          title: toastTitle || '修改成功'
        })
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    _execLoadData (callback) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        const {
          data,
          count
        } = res.result
        this._isEnded = data.length < this.pageSize
        this.hasMore = !this._isEnded

        const data2 = this.getone ? (data.length ? data[0] : undefined) : data

        callback && callback(data2, this._isEnded)
        this._dispatchEvent(events.load, data2)

        if (this.getone || this.pageData === pageMode.replace) {
          this.dataList = data2
        } else {
          this.dataList.push(...data2)
          if (this.dataList.length) {
            this.paginationInternal.current++
          }
        }

        if (this.getcount) {
          this.paginationInternal.count = count
        }

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this.errorMessage = err
        callback && callback()
        this.$emit(events.error, err)
        if (process.env.NODE_ENV === 'development') {
          console.error(err)
        }
      })
    },
    _getExec () {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(this.collection)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.groupby) {
        db = db.groupby(this.groupby)
      }
      if (this.groupField) {
        db = db.groupField(this.groupField)
      }
      if (this.distinct === true) {
        db = db.distinct()
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      if (this.gettree) {
        getOptions.getTree = {
          limitLevel: this.limitlevel,
          startWith: this.startwith
        }
      }
      db = db.skip(size * (current - 1)).limit(size).get(getOptions)

      return db
    },
    _execRemove (id, action, success, fail, complete) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      uni.showLoading({
        mask: true
      })

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this.collection).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded)
      } else {
        this.$emit(type, data, this._isEnded)
      }
    }
  }
}
</script>

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: String,
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    manual: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {
        current: this.pageCurrent,
        size: this.pageSize,
        count: 0
      },
      errorMessage: ''
    }
  },
  created () {
    this._isEnded = false

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      if (typeof args1 === 'object') {
        if (args1.clear) {
          this.clear()
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback)
    },
    loadMore () {
      if (this._isEnded) {
        return
      }
      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      toastTitle,
      success,
      fail,
      complete
    } = {}) {
      uni.showLoading()
      /* eslint-disable no-undef */
      const db = uniCloud.database()
      db.collection(this.collection).add(value).then((res) => {
        success && success(res)
        uni.showToast({
          title: toastTitle || '新增成功'
        })
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent
    } = {}) {
      if (!id || !id.length) {
        return
      }
      uni.showModal({
        title: confirmTitle || '提示',
        content: confirmContent || '是否删除该数据',
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete)
        }
      })
    },
    update (id, value, {
      toastTitle,
      success,
      fail,
      complete
    } = {}) {
      uni.showLoading()
      /* eslint-disable no-undef */
      const db = uniCloud.database()
      return db.collection(this.collection).doc(id).update(value).then((res) => {
        success && success(res)
        uni.showToast({
          title: toastTitle || '修改成功'
        })
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    _execLoadData (callback) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        const {
          data,
          count
        } = res.result
        this._isEnded = data.length < this.pageSize
        this.hasMore = !this._isEnded

        const data2 = this.getone ? (data.length ? data[0] : undefined) : data

        callback && callback(data2, this._isEnded)
        this._dispatchEvent(events.load, data2)

        if (this.getone || this.pageData === pageMode.replace) {
          this.dataList = data2
        } else {
          this.dataList.push(...data2)
          if (this.dataList.length) {
            this.paginationInternal.current++
          }
        }

        if (this.getcount) {
          this.paginationInternal.count = count
        }

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this.errorMessage = err
        callback && callback()
        this.$emit(events.error, err)
        if (process.env.NODE_ENV === 'development') {
          console.error(err)
        }
      })
    },
    _getExec () {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(this.collection)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      if (this.gettree) {
        getOptions.getTree = {
          limitLevel: this.limitlevel,
          startWith: this.startwith
        }
      }
      db = db.skip(size * (current - 1)).limit(size).get(getOptions)

      return db
    },
    _execRemove (id, action, success, fail, complete) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      uni.showLoading({
        mask: true
      })

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this.collection).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        uni.showModal({
          content: err.message,
          showCancel: false
        })
      }).finally(() => {
        uni.hideLoading()
        complete && complete()
      })
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded)
      } else {
        this.$emit(type, data, this._isEnded)
      }
    }
  }
}
</script>