<template>
	<div class="system-task">
		<el-row class="box" type="flex" :gutter="10">
			<el-col v-for="(item, index) in list" :key="index">
				<div class="block" :class="[`_${item.key}`]">
					<div class="header">
						<i class="icon" :class="item.icon"></i>
						<span class="label">{{ item.label }}</span>
						<span class="num">({{ item.pagination.total }})</span>
						<span class="flex1"></span>
						<ul class="op-btn">
							<li v-if="item.loading">
								<i class="el-icon-loading"></i>
							</li>

							<li
								v-else
								@click="refreshTask({ page: 1 })"
								class="refresh-btn"
								v-permission="perm.delete"
							>
								<i class="el-icon-refresh"></i>
								<span>刷新</span>
							</li>

							<li @click="edit(item.params)" class="add-btn" v-permission="perm.add">
								<i class="el-icon-plus"></i>
								<span>添加</span>
							</li>
						</ul>
					</div>

					<div class="container scroller1">
						<draggable
							v-model="list[index].list"
							v-bind="drag.options"
							tag="ul"
							:data-type="item.params.type"
							:data-status="item.params.status"
							@end="e => onDragEnd(e, item)"
						>
							<li
								v-for="item2 in list[index].list"
								:key="item2.id"
								:data-id="item2.id"
								@contextmenu.stop.prevent="openCM($event, item2)"
								class="_drag"
							>
								<div class="h">
									<span class="type _warning" v-show="item2.status === 0">{{
										item2.type | task_type
									}}</span>
									<span class="name">{{ item2.name }} </span>
								</div>

								<div class="remark">{{ item2.remark }}</div>

								<div class="f">
									<template v-if="item2.status">
										<span class="date">{{ item2.nextRunTime || '...' }}</span>
										<span class="start">进行中</span>
									</template>

									<template v-else>
										<span>...</span>
										<span class="stop">已停止</span>
									</template>
								</div>

								<el-row type="flex" class="op">
									<el-col v-if="item2.status === 0" @click.native="start(item2)">
										<i class="el-icon-video-play"></i>
										<span>开始</span>
									</el-col>

									<el-col
										v-else
										@click.native="stop(item2)"
										v-permission="perm.stop"
									>
										<i class="el-icon-video-pause"></i>
										<span>暂停</span>
									</el-col>

									<el-col
										@click.native="edit(item2)"
										v-permission="{
											and: [perm.update, perm.info]
										}"
									>
										<i class="el-icon-edit"></i>
										<span>编辑</span>
									</el-col>

									<el-col @click.native="findLog(item2)" v-permission="perm.log">
										<i class="el-icon-tickets"></i>
										<span>查看日志</span>
									</el-col>
								</el-row>
							</li>

							<li v-if="list[index].list.length == 0">
								<div class="empty">暂无数据</div>
							</li>
						</draggable>

						<el-button
							class="more"
							type="text"
							size="mini"
							@click="moreTask(index, item)"
							v-if="item.pagination.total >= item.pagination.size"
							>查看更多</el-button
						>
					</div>

					<div class="footer">
						<button class="btn-add" @click="edit(item.params)" v-permission="perm.add">
							<i class="el-icon-plus"></i>
						</button>
					</div>
				</div>
			</el-col>

			<el-col v-permission="perm.log">
				<div class="block _log">
					<div class="header">
						<span class="label">日志</span>
						<span class="num">({{ logs.pagination.total }})</span>
						<span class="flex1"></span>

						<el-checkbox-group
							class="status"
							v-model="logs.filters.status"
							@change="filterLog"
						>
							<el-checkbox :label="0">异常</el-checkbox>
						</el-checkbox-group>

						<ul class="op-btn">
							<li @click="refreshLog({ page: 1 })">
								<i class="el-icon-refresh"></i>
								<span>刷新</span>
							</li>

							<li v-if="logs.current" class="_current-log" @click="allLog">
								<span>{{ logs.current.name }}</span>
								<i class="el-icon-close"></i>
							</li>
						</ul>
					</div>

					<div
						class="container"
						v-loading="logs.loading"
						element-loading-text="拼命加载中"
					>
						<ul class="_scroller" v-infinite-scroll="moreLog">
							<li
								v-for="(item, index) in logs.list"
								:key="index"
								:class="{ _error: item.status == 0 }"
								@click="expandLog(item)"
							>
								<div class="h">
									<span class="name">{{ index + 1 }} · {{ item.taskName }}</span>
								</div>

								<div class="remark" :class="{ _ellipsis: !item._expand }">
									{{ item.detail || '...' }}
								</div>

								<div class="f">
									<span>执行时间：{{ item.createTime }}</span>
								</div>
							</li>

							<li v-if="logs.list.length == 0">
								<div class="empty">暂无数据</div>
							</li>
						</ul>
					</div>
				</div>
			</el-col>
		</el-row>

		<cl-context-menu ref="context-menu"> </cl-context-menu>
		<cl-form ref="cl-form"></cl-form>
	</div>
</template>

<script>
import draggable from 'vuedraggable';
import { checkPerm, utils } from '@/cool';

export default {
	name: 'system-task',

	components: {
		draggable
	},

	data() {
		return {
			list: [
				{
					key: 'sys',
					label: '系统任务',
					icon: 'el-icon-s-tools',
					list: [],
					loading: false,
					params: {
						type: 0,
						status: 1
					},
					pagination: {
						size: 10,
						page: 1,
						total: 0
					}
				},
				{
					key: 'user',
					label: '用户自定义任务',
					icon: 'el-icon-user-solid',
					list: [],
					loading: false,
					params: {
						type: 1,
						status: 1
					},
					pagination: {
						size: 10,
						page: 1,
						total: 0
					}
				},
				{
					key: 'stop',
					label: '已停止任务',
					list: [],
					loading: false,
					params: {
						type: null,
						status: 0
					},
					pagination: {
						size: 10,
						page: 1,
						total: 0
					}
				}
			],
			logs: {
				loading: false,
				list: [],
				pagination: {
					size: 10,
					page: 1
				},
				params: {},
				filters: {
					status: []
				},
				current: null
			},
			drag: {
				options: {
					group: 'Task',
					animation: 300,
					ghostClass: 'Ghost',
					dragClass: 'Drag',
					draggable: '._drag'
				}
			}
		};
	},

	filters: {
		task_type(i) {
			return i === 0 ? '系统' : '用户';
		}
	},

	mounted() {
		this.refreshTask({ page: 1 });
	},

	computed: {
		perm() {
			return this.$service.system.task.permission;
		}
	},

	methods: {
		onDragEnd({ to, item }) {
			const status = to.getAttribute('data-status');
			const type = to.getAttribute('data-type');
			const id = item.getAttribute('data-id');

			if (status == 0) {
				this.stop({ id });
			}

			if (status == 1) {
				this.start({ id, type });
			}
		},

		openCM(e, { id, status, type, name }) {
			let menus = [
				{
					label: '立即执行',
					perm: ['once'],
					'suffix-icon': 'el-icon-video-play',
					callback: (e, close) => {
						this.once({ id });
						close();
					}
				},
				{
					label: '编辑',
					perm: ['update', 'info'],
					'suffix-icon': 'el-icon-edit',
					callback: (e, close) => {
						this.edit({ id, type });
						close();
					}
				},
				{
					label: '删除',
					perm: ['_delete'],
					'suffix-icon': 'el-icon-delete',
					callback: (e, close) => {
						this.delete({ id });
						close();
					}
				},
				{
					label: '查看日志',
					perm: ['log'],
					'suffix-icon': 'el-icon-tickets',
					callback: (e, close) => {
						this.findLog({ id, name });
						close();
					}
				}
			];

			if (status == 1) {
				menus.splice(1, 0, {
					label: '暂停',
					perm: ['stop'],
					'suffix-icon': 'el-icon-video-pause',
					callback: (e, close) => {
						this.stop({ id, type });
						close();
					}
				});
			} else {
				menus.splice(1, 0, {
					label: '开始',
					perm: ['start'],
					'suffix-icon': 'el-icon-video-play',
					callback: (e, close) => {
						this.start({ id, type });
						close();
					}
				});
			}

			this.$refs['context-menu'].open(e, {
				list: menus.filter(e => {
					return checkPerm({
						and: e.perm.map(a => this.perm[a])
					});
				})
			});

			return false;
		},

		async edit(params) {
			const { id, type } = params || {};

			let info = {
				type
			};

			if (id) {
				info = await this.$service.system.task.info({ id });
			}

			if (info.every) {
				info.every /= 1000;
			}

			if (!info.limit) {
				info.limit = undefined;
			}

			const isCron = info.taskType ? false : true;

			const form = this.$refs['cl-form'].open({
				props: {
					title: `编辑任务`,
					width: '600px',
					'label-width': '80px'
				},

				items: [
					{
						label: '名称',
						prop: 'name',
						value: info.name,
						component: {
							name: 'el-input',
							attrs: {
								placeholder: '请输入名称'
							}
						},
						rules: {
							required: true,
							message: '名称不能为空'
						}
					},
					{
						label: '类型',
						prop: 'taskType',
						value: info.taskType || 0,
						component: {
							name: 'el-select',
							options: [
								{
									label: 'cron',
									value: 0
								},
								{
									label: '时间间隔',
									value: 1
								}
							],
							on: {
								change: v => {
									form.items.map(e => {
										if (e.prop == 'cron') {
											e.hidden = v !== 0;
										}

										if (['limit', 'every'].includes(e.prop)) {
											e.hidden = v !== 1;

											if (!e.value) {
												form[e.prop] = undefined;
											}
										}
									});
								}
							}
						}
					},
					{
						label: 'cron',
						prop: 'cron',
						hidden: !isCron,
						value: info.cron,
						component: {
							name: 'el-input',
							attrs: {
								placeholder: '* * * * * *'
							}
						},
						rules: {
							required: true,
							message: 'cron不能为空'
						}
					},
					{
						label: '次数',
						prop: 'limit',
						value: info.limit,
						hidden: isCron,
						component: {
							name: 'el-input-number',
							props: {
								min: 1,
								max: 10000
							}
						}
					},
					{
						label: '间隔(秒)',
						prop: 'every',
						value: info.every,
						hidden: isCron,
						component: {
							name: 'el-input-number',
							props: {
								min: 1,
								max: 100000000
							}
						},
						rules: {
							required: true,
							message: '执行间隔不能为空'
						}
					},
					{
						label: 'service',
						prop: 'service',
						value: info.service,
						component: {
							name: 'el-input',
							attrs: {
								placeholder: 'sys.test.add(params)'
							}
						}
					},
					{
						label: '开始时间',
						prop: 'startDate',
						value: info.startDate,
						component: {
							name: 'el-date-picker',
							props: {
								type: 'datetime'
							}
						}
					},
					{
						label: '结束时间',
						prop: 'endDate',
						value: info.endDate,
						component: {
							name: 'el-date-picker',
							props: {
								type: 'datetime'
							}
						}
					},
					{
						label: '备注',
						prop: 'remark',
						value: info.remark,
						component: {
							name: 'el-input',
							props: {
								type: 'textarea'
							}
						}
					},
					{
						label: '状态',
						prop: 'status',
						value: info.status === 0 ? 0 : 1,
						component: {
							name: 'el-radio-group',
							options: [
								{
									label: '停止',
									value: 0
								},
								{
									label: '运行',
									value: 1
								}
							]
						}
					}
				],

				on: {
					submit: ({ data, close, done }) => {
						if (!data.limit) {
							data.limit = null;
						}

						this.$service.system.task[id ? 'update' : 'add']({
							...info,
							...data,
							every: data.every * 1000
						})
							.then(() => {
								this.refreshTask();

								this.$message.success('保存成功');
								close();
							})
							.catch(err => {
								this.$message.error(err);
								done();
							});
					}
				}
			});
		},

		delete({ id }) {
			this.$confirm('此操作将永久删除该数据，是否继续？', '提示', {
				type: 'warning'
			})
				.then(() => {
					this.$service.system.task.delete({ ids: id }).then(() => {
						this.refreshTask();
					});
				})
				.catch(() => {});
		},

		start({ id, type }) {
			this.$service.system.task
				.start({ id, type })
				.then(() => {
					this.refreshTask();
				})
				.catch(err => {
					this.$message.error(err);
				});
		},

		stop({ id }) {
			this.$service.system.task
				.stop({ id })
				.then(() => {
					this.refreshTask();
				})
				.catch(err => {
					this.$message.error(err);
				});
		},

		once({ id }) {
			this.$service.system.task
				.once({ id })
				.then(() => {
					this.refreshTask();
				})
				.catch(err => {
					this.$message.error(err);
				});
		},

		expandLog(e) {
			this.$set(e, '_expand', !e._expand);
		},

		refreshTask(params, options) {
			const { index, more } = options || {};
			const arr =
				index === undefined || index === null ? this.list.map((e, i) => i) : [index];

			arr.forEach(async k => {
				let item = this.list[k];

				Object.assign(item.params, {
					...item.pagination,
					...params
				});

				this.$set(item, 'loading', true);

				let res = await this.$service.system.task.page(item.params);

				utils.moreList(res, item);

				if (!more) {
					this.$el.querySelector(`.block._${item.key} .container`).scroll(0, 0);
				}

				item.loading = false;
			});
		},

		moreTask(index) {
			this.refreshTask(null, { index, more: true });
		},

		async refreshLog(newParams, options) {
			if (this.logs.loading) {
				return false;
			}

			if (!checkPerm(this.perm.log)) {
				return false;
			}

			const { params, pagination } = this.logs;
			const { more } = options || {};

			Object.assign(params, {
				...pagination,
				...newParams
			});

			this.logs.loading = true;

			let res = await this.$service.system.task.log(params);

			utils.moreList(res, this.logs);

			if (!more) {
				this.$el.querySelector('.block._log .container ul').scroll(0, 0);
			}

			this.logs.loading = false;
		},

		moreLog() {
			this.refreshLog(null, { more: true });
		},

		findLog(e) {
			this.logs.current = e;
			this.refreshLog({ page: 1, id: e.id });
		},

		allLog() {
			this.logs.current = null;
			this.refreshLog({ page: 1, id: null });
		},

		filterLog([v]) {
			this.refreshLog({ page: 1, status: v === undefined ? 1 : 0 });
		}
	}
};
</script>

<style lang="stylus" scoped>
.Ghost {
	opacity: 0.7;
}

.Drag {
	border: 1px dashed #000 !important;
	background: #fff !important;
}

.system-task {
	.box {
		height: 100%;
		overflow-x: auto;

		.el-col {
			height: 100%;
			width: 413px;
		}
	}

	.block {
		height: 100%;
		width: 400px;

		.header {
			display: flex;
			align-items: center;
			height: 40px;
			background-color: #f0f0f0;
			border-top-left-radius: 10px;
			border-top-right-radius: 10px;
			position: relative;
			top: 5px;
			z-index: 1;
			padding: 0 10px 5px 10px;

			i {
				font-size: 18px;
			}

			.label {
				font-size: 12px;
				margin: 0 5px;
				letter-spacing: 0.5px;
			}

			.num {
				font-size: 12px;
			}

			.flex1 {
				flex: 1;
			}

			.op-btn {
				display: flex;
				align-items: center;

				li {
					list-style: none;
					cursor: pointer;
					padding: 2px 10px;
					background-color: #fff;
					border-radius: 3px;
					margin-left: 5px;

					&:hover {
						background-color: #dedede;
						color: #444;
					}

					i {
						font-size: 14px;
						margin-right: 2px;
					}

					span {
						font-size: 12px;
					}
				}
			}
		}

		.container {
			max-height: calc(100% - 90px);
			overflow-y: auto;
			margin-bottom: 5px;
			z-index: 2;
			position: relative;
			background-color: #f7f7f7;

			ul {
				li {
					list-style: none;
					background-color: #fff;
					border-radius: 5px;
					margin-bottom: 5px;
					padding: 10px 15px;
					font-size: 14px;
					letter-spacing: 0.5px;
					border: 1px solid #f7f7f7;

					&:last-child {
						margin-bottom: 0;
					}

					&._drag {
						cursor: pointer;
					}

					&:hover {
						.op {
							height: 30px;
						}
					}

					.h {
						display: flex;
						align-items: center;
						font-size: 14px;
						margin-bottom: 10px;

						.type {
							font-size: 12px;
							border-radius: 3px;
							padding: 1px 2px;
							margin-right: 5px;

							&._warning {
								background-color: $color-warning;
								color: #fff;
							}
						}
					}

					.remark {
						font-size: 12px;
						color: #666;
						margin-bottom: 20px;
					}

					.empty {
						text-align: center;
						font-size: 13px;
						color: #666;
						margin: 10px 0;
					}

					.f {
						display: flex;
						align-items: center;
						justify-content: space-between;
						position: relative;

						.date {
							font-size: 12px;
							color: #fff;
							background-color: #1b9aee;
							border-radius: 2px;
							margin-left: 40px;
							padding: 1px 3px;

							&::before {
								content: 'NEXT';
								position: absolute;
								left: 0;
								top: 1px;
								color: #222;
							}
						}

						.start, .stop {
							display: flex;
							align-items: center;
							font-size: 12px;
							margin-left: 30px;
							position: relative;

							&::before {
								content: '';
								display: block;
								height: 6px;
								width: 6px;
								border-radius: 6px;
								position: absolute;
								left: -15px;
							}
						}

						.start {
							color: #67C23A;

							&::before {
								background-color: #67C23A;
							}
						}

						.stop {
							color: #F56C6C;

							&::before {
								background-color: #F56C6C;
							}
						}
					}

					.op {
						height: 0;
						margin-top: 15px;
						transition: height 0.3s;
						overflow: hidden;

						.el-col {
							height: 30px;
							display: flex;
							justify-content: center;
							align-items: center;

							&:hover {
								background-color: #f7f7f7;
							}

							span {
								font-size: 12px;
								color: #666;
							}

							i {
								font-size: 16px;
								margin-right: 5px;
							}
						}
					}

					&._error {
						background-color: $color-danger;
						color: #fff;

						.remark {
							color: #fff !important;
						}
					}
				}
			}

			.more {
				display: block;
				margin: 10px auto;
			}
		}

		.footer {
			height: 36px;

			.btn-add {
				height: 34px;
				width: 100%;
				border-radius: 3px;
				border: 0;
				background-color: #fff;
				cursor: pointer;

				i {
					font-size: 16px;
					color: #999;
				}

				&:hover {
					box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
				}
			}
		}
	}

	.block._stop {
		.header {
			.add-btn {
				display: none;
			}
		}

		.container {
			max-height: calc(100% - 50px);
		}

		.footer {
			display: none;
		}
	}

	.block._log {
		.header {
			.status {
				.el-checkbox {
					margin-right: 10px;
				}
			}

			.op-btn {
				li {
					&._current-log {
						i {
							margin-left: 2px;
						}

						&:hover {
							i {
								color: $color-danger;
							}
						}
					}
				}
			}
		}

		.container {
			height: calc(100% - 50px);
			max-height: calc(100% - 50px);
			overflow-y: hidden;

			ul {
				height: 100%;
				overflow-y: auto;

				li {
					.remark {
						color: #999;

						&._ellipsis {
							overflow: hidden;
							text-overflow: ellipsis;
							display: -webkit-box;
							-webkit-box-orient: vertical;
							-webkit-line-clamp: 2;
						}
					}

					.f {
						font-size: 12px;
					}

					&:hover {
						.remark {
							color: #444;
						}
					}
				}
			}
		}
	}
}
</style>
