<template>
	<el-container>
		<el-header>
			<div class="left-panel">
				<el-button type="primary" style="margin-right:4px" @click="()=>{dialogVisible=true;}">发起流程</el-button>
				<el-segmented
					v-model="tvalue"
					:options="Object.keys(toptions)"
					size="default"
					@change="params.category = toptions[tvalue]; handleQuery()"
				></el-segmented>
				<el-select
					v-model="query.workflow"
					placeholder="审批流"
					@change="handleQuery"
					clearable
					style="margin-left: 2px"
				>
					<el-option-group
					v-for="group in wfOptions"
					:key="group.category"
					:label="group.category"
				>
					<el-option
						v-for="item in group.items"
						:key="item.id"
						:label="item.name"
						:value="item.id"
					></el-option>
				</el-option-group>
				</el-select>
				<el-select
					v-model="query.script_run_last_result"
					placeholder="执行状态"
					@change="handleQuery"
					clearable
					style="margin-left: 2px; width:100px"
				>
					<el-option
						v-for="item in rsOptions"
						:key="item.value"
						:label="item.label"
						:value="item.value"
					></el-option>
				</el-select>
				<el-date-picker
					v-model="timeRange"
					type="datetimerange"
					range-separator="至"
					start-placeholder="创建时间始"
					end-placeholder="创建时间止"
					style="margin-left: 2px"
					value-format="YYYY-MM-DD HH:mm:ss"
					@change="handleQuery"
					clearable
				/>
			</div>
		</el-header>
		<el-main class="nopadding">
			<scTable
				ref="table"
				:apiObj="apiObj"
				:params="params"
				row-key="id"
				stripe
				highlightCurrentRow
			>
				<el-table-column
					label="流水号"
					prop="sn"
					width="180"
				>
				<template #default="scope">
					<el-link
						@click="handleShow(scope.row)" type="primary"
						>{{ scope.row.sn }}</el-link>
				</template>
				</el-table-column>
				<el-table-column
					label="工单标题"
					prop="title"
					min-width="180"
					:show-overflow-tooltip="true"
				></el-table-column>

				<el-table-column label="工作流" prop="title" width="160">
					<template #default="scope">
						{{ scope.row.workflow_.name }}
					</template>
				</el-table-column>
				<el-table-column label="节点状态"  width="300">
					<template #default="scope">
											<el-tag :type="actStateEnum[scope.row.act_state]?.type">
									{{ actStateEnum[scope.row.act_state]?.text }}
								</el-tag>
						<el-tag type="info" effect="plain">{{ scope.row.state_.name }}</el-tag>

					</template>
        		</el-table-column>
				<el-table-column label="可处理人" :show-overflow-tooltip="true" width="200">
					<template #default="scope">
						<span
							v-if="
								scope.row.participant_type == 2 ||
								scope.row.participant_type == 1
							"
						>
							<span
								v-for="item in scope.row.participant_"
								:key="item.id"
								>{{ item.name }}/</span
							>
						</span>
						<span v-else> 无 </span>
					</template>
				</el-table-column>
				<el-table-column
					label="创建时间"
					prop="create_time"
					width="150"
				></el-table-column>
				<el-table-column label="操作" fixed="right" align="center" width="80">
					<template #default="scope">
						<el-button
							type="danger"
							size="small"
							@click="reStart(scope.row)"
							v-if="scope.row.script_run_last_result == false"
							>重试</el-button
						>
					</template>
				</el-table-column>
			</scTable>
		</el-main>
		<el-drawer v-model="drawer" size="90%" :show-close="false">
			<template #header="{ close, titleId, titleClass }">
				<h4 :id="titleId" :class="titleClass">{{drawerName}}</h4>
				<el-button type="danger" @click="close">关闭</el-button>
			</template>
			<component :is="currentComponent" :ticketId="ticketId" :t_id="t_id" @closed="drawer = false"
			@success="handleSuccess"></component>
		</el-drawer>
	</el-container>
	<el-dialog
		v-model="dialogVisible"
		title="选择流程"
		width="960px"
		top="6vh"
		class="wf-picker-dialog"
		:close-on-click-modal="false"
		destroy-on-close
	>
		<div class="wf-picker">
			<aside class="wf-picker__aside">
				<div class="wf-picker__search">
					<el-input
						v-model="wfKeyword"
						placeholder="搜索流程"
						clearable
						:prefix-icon="Search"
					/>
				</div>
				<nav class="wf-picker__nav">
					<a
						v-for="group in filteredWfOptions"
						:key="group.category"
						class="wf-picker__nav-item"
						:class="{ active: activeCategory === group.category }"
						@click="scrollToCategory(group.category)"
					>
						<el-icon class="wf-picker__nav-icon">
							<component :is="categoryIcon(group.category)" />
						</el-icon>
						<span class="wf-picker__nav-text">{{ group.category }}</span>
						<span class="wf-picker__nav-count">{{ group.items.length }}</span>
					</a>
				</nav>
			</aside>

			<section ref="wfScroll" class="wf-picker__body" @scroll="onPickerScroll">
				<template v-if="filteredWfOptions.length">
					<div
						v-for="group in filteredWfOptions"
						:key="group.category"
						class="wf-picker__group"
						:data-category="group.category"
						:ref="el => setGroupRef(group.category, el)"
					>
						<header class="wf-picker__group-header">
							<el-icon class="wf-picker__group-icon">
								<component :is="categoryIcon(group.category)" />
							</el-icon>
							<h3 class="wf-picker__group-title">{{ group.category }}</h3>
							<span class="wf-picker__group-divider"></span>
							<span class="wf-picker__group-meta">{{ group.items.length }} 项</span>
						</header>
						<div class="wf-picker__grid">
							<button
								v-for="item in group.items"
								:key="item.id"
								type="button"
								class="wf-picker__card"
								@click="startTicket(item)"
							>
								<span class="wf-picker__card-icon">
									<el-icon><component :is="itemIcon(item.name, group.category)" /></el-icon>
								</span>
								<span class="wf-picker__card-name">{{ item.name }}</span>
								<span class="wf-picker__card-arrow">
									<el-icon><ArrowRight /></el-icon>
								</span>
							</button>
						</div>
					</div>
				</template>
				<el-empty v-else description="未找到匹配的流程" :image-size="80" />
			</section>
		</div>
	</el-dialog>
</template>

<script>
import { defineAsyncComponent, markRaw } from 'vue'; // 异步组件加载
import { actStateEnum, interveneTypeEnum } from "@/utils/enum.js";
import {
	Search, ArrowRight,
	UserFilled, SwitchButton, CircleClose,
	Tools, Box, Postcard, Promotion, ChatLineRound,
	Van, Monitor, OfficeBuilding, Document, Briefcase, ShoppingCart,
	Cpu, Files, Grid, Tickets
} from "@element-plus/icons-vue";

const CATEGORY_ICON_MAP = {
	"人事": UserFilled,
	"设备": Tools,
	"固资": Box,
	"行政": Briefcase,
	"科研": Cpu,
	"采购": ShoppingCart,
	"未分组": Grid,
};

const ITEM_ICON_RULES = [
	[/合同|协议/, Document],
	[/调岗|调动|交接/, SwitchButton],
	[/用人|招聘|入职/, UserFilled],
	[/离职|辞职/, CircleClose],
	[/维修|检修|故障/, Tools],
	[/入库|出库|盘点|资产/, Box],
	[/档案|文件|借阅/, Files],
	[/印章|盖章/, Postcard],
	[/宣传|报道|发布/, Promotion],
	[/会议|预定/, ChatLineRound],
	[/用车|车辆|出行/, Van],
	[/平台|系统|监控/, Monitor],
	[/供应商|采购|订单/, OfficeBuilding],
	[/请假|加班|考勤/, Tickets],
	[/审批|报销|申请/, Document],
];

export default {
	name: "myticket",
	components: {
		Search, ArrowRight,
		UserFilled, SwitchButton, CircleClose,
		Tools, Box, Postcard, Promotion, ChatLineRound,
		Van, Monitor, OfficeBuilding, Document, Briefcase, ShoppingCart,
		Cpu, Files, Grid, Tickets
	},
	data() {
		return {
			Search: markRaw(Search),
			ArrowRight: markRaw(ArrowRight),
			actStateEnum, interveneTypeEnum,
			wfKeyword: "",
			activeCategory: "",
			wfGroupRefs: {},
			drawerName: "工单详情",
			drawer: false,
			tvalue: "待办",
			toptions: {
				"待办": "duty",
				"我的": "owner",
				"已处理": "worked",
				"抄送我": "cc"
			},
			query: {},
			timeRange: [],
			list: [],
			apiObj: null,
			params: { category: "duty" },
			act_states: {
				0: "草稿中",
				1: "进行中",
				2: "被退回",
				3: "被撤回",
				4: "已完成",
				5: "已关闭",
			},
			participant_: {
				0: "无处理人",
				1: "单人",
				2: "多人",
			},
			rsOptions: [
				{ value: true, label: "成功" },
				{ value: false, label: "失败" },
			],
			wfOptions: [],
			currentComponent: null,
			ticketId: null,
			t_id: null,
			dialogVisible: false
		};
	},
	mounted() {
		let category = this.$route.query.category
		if (category) {
			this.tvalue = category == "duty" ? "待办" : category == "owner" ? "我的" : category == "worked" ? "已处理" : category == "cc" ? "抄送我" : "待办"
			this.params.category = category
			this.apiObj = this.$API.wf.ticket.list
		} else {
			this.tvalue = "待办"
			this.params.category = "duty"
			this.apiObj = this.$API.wf.ticket.list
		}
		this.getWfOptions();
	},
	computed: {
		filteredWfOptions() {
			const kw = (this.wfKeyword || "").trim().toLowerCase();
			if (!kw) return this.wfOptions;
			return this.wfOptions
				.map(g => ({
					category: g.category,
					items: g.items.filter(it =>
						(it.name || "").toLowerCase().includes(kw) ||
						(g.category || "").toLowerCase().includes(kw)
					)
				}))
				.filter(g => g.items.length);
		},
	},
	watch: {
		dialogVisible(v) {
			if (v) {
				this.wfKeyword = "";
				this.$nextTick(() => {
					this.activeCategory = this.filteredWfOptions[0]?.category || "";
				});
			}
		},
	},
	methods: {
		categoryIcon(cate) {
			return CATEGORY_ICON_MAP[cate] || Grid;
		},
		itemIcon(name, cate) {
			for (const [re, icon] of ITEM_ICON_RULES) {
				if (re.test(name)) return icon;
			}
			return CATEGORY_ICON_MAP[cate] || Document;
		},
		setGroupRef(category, el) {
			if (el) this.wfGroupRefs[category] = el;
		},
		scrollToCategory(category) {
			const el = this.wfGroupRefs[category];
			const scroller = this.$refs.wfScroll;
			if (!el || !scroller) return;
			this.activeCategory = category;
			scroller.scrollTo({
				top: el.offsetTop - 8,
				behavior: "smooth",
			});
		},
		onPickerScroll() {
			const scroller = this.$refs.wfScroll;
			if (!scroller) return;
			const top = scroller.scrollTop;
			let current = this.filteredWfOptions[0]?.category || "";
			for (const g of this.filteredWfOptions) {
				const el = this.wfGroupRefs[g.category];
				if (el && el.offsetTop - 24 <= top) current = g.category;
			}
			if (current !== this.activeCategory) this.activeCategory = current;
		},
		async getWfOptions() {
			let permissions = this.$TOOL.data.get("PERMISSIONS");
			let userInfo = this.$TOOL.data.get("USER_INFO");
			let isDeptHead = false;
			try {
				const userPosts = await this.$API.system.userPost.list.req({ user: userInfo.id, page: 0 });
				isDeptHead = (userPosts || []).some(up => up.post_ && up.post_.name === "部门负责人");
			} catch (e) {
				console.error("获取用户岗位失败:", e);
			}
			const restrictedKeys = { wf_leave: isDeptHead, wf_patent: isDeptHead, wf_paperse: isDeptHead };
			const groups = {};
			this.$API.wf.workflow.list.req({ page: 0 }).then((res) => {
				res.forEach((item) => {
				    if(item.key && permissions.includes(item.key)) {
						if (item.key in restrictedKeys && !restrictedKeys[item.key]) {
							return;
						}
						let cate = item.cate;
						if (!cate){cate="未分组"}
						if (!groups[cate]) {
							groups[cate] = [];
						}
						groups[cate].push(item);
					}
				})
				// 转换为数组形式，便于模板遍历
			this.wfOptions =  Object.keys(groups).map(category => ({
				category,
				items: groups[category]
			}));
			});
		},
		reStart(row) {
			this.$API.wf.ticket.retryScript.req(row.id).then((res) => {
				this.$message.success("任务执行下发成功");
				row.script_run_last_result = true;
			});
		},
		handleQuery() {
			if (this.timeRange) {
				this.query.start_create = this.timeRange[0];
				this.query.end_create = this.timeRange[1];
			} else {
				this.query.end_create = null;
				this.query.start_create = null;
			}
			this.$refs.table.queryData(this.query);
		},
		handleShow(row) {
			this.drawer = true;
			this.ticketId = row.id;
			this.t_id = row.ticket_data.t_id;
			this.drawerName = row.title;
			const viewPath = row.workflow_.view_path;
			// 动态 import
			this.currentComponent = markRaw(
			defineAsyncComponent(() => import(`@/views${viewPath}.vue`))
			);
		},
		startTicket(item) {
			this.dialogVisible = false;
			this.drawer = true;
			this.t_id = null;
			this.drawerName = item.name;
			const viewPath = item.view_path;
			// 动态 import
			this.currentComponent = markRaw(
				defineAsyncComponent(() => import(`@/views${viewPath}.vue`))
			);
		},
		handleSuccess(mode) {
		    this.drawer = false; 
			if (mode == "add") {
				this.tvalue = "我的";
				this.params.category = "owner";
				this.query = {};
				this.$refs.table.scrollToTop();
			}
			this.handleQuery();
		}
	},
};
</script>

<style scoped>
/* ===== 选择流程对话框 ===== */
:deep(.wf-picker-dialog) {
	border-radius: 14px;
	overflow: hidden;
}
:deep(.wf-picker-dialog .el-dialog__header) {
	padding: 18px 24px;
	margin-right: 0;
	border-bottom: 1px solid var(--el-border-color-lighter);
	background: linear-gradient(
		180deg,
		var(--el-fill-color-blank) 0%,
		var(--el-fill-color-light) 100%
	);
}
:deep(.wf-picker-dialog .el-dialog__title) {
	font-size: 16px;
	font-weight: 600;
	letter-spacing: 0.5px;
}
:deep(.wf-picker-dialog .el-dialog__body) {
	padding: 0;
}

.wf-picker {
	display: flex;
	height: 66vh;
	min-height: 440px;
	max-height: 640px;
	background: var(--el-bg-color);
}

/* 左侧分类导航 */
.wf-picker__aside {
	width: 200px;
	flex-shrink: 0;
	display: flex;
	flex-direction: column;
	border-right: 1px solid var(--el-border-color-lighter);
	background: var(--el-fill-color-light);
}
.wf-picker__search {
	padding: 14px 12px 10px;
}
.wf-picker__nav {
	flex: 1;
	overflow-y: auto;
	padding: 4px 8px 16px;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.wf-picker__nav-item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 12px;
	border-radius: 8px;
	font-size: 13.5px;
	color: var(--el-text-color-regular);
	cursor: pointer;
	user-select: none;
	transition:
		background-color 0.18s ease,
		color 0.18s ease,
		transform 0.18s ease;
	position: relative;
}
.wf-picker__nav-item::before {
	content: "";
	position: absolute;
	left: 4px;
	top: 50%;
	transform: translateY(-50%) scaleY(0);
	width: 3px;
	height: 18px;
	background: var(--el-color-primary);
	border-radius: 2px;
	transition: transform 0.2s ease;
}
.wf-picker__nav-item:hover {
	background: var(--el-fill-color);
	color: var(--el-text-color-primary);
}
.wf-picker__nav-item.active {
	background: var(--el-color-primary-light-9);
	color: var(--el-color-primary);
	font-weight: 600;
}
.wf-picker__nav-item.active::before {
	transform: translateY(-50%) scaleY(1);
}
.wf-picker__nav-icon {
	font-size: 16px;
	flex-shrink: 0;
}
.wf-picker__nav-text {
	flex: 1;
}
.wf-picker__nav-count {
	font-size: 12px;
	padding: 1px 7px;
	border-radius: 10px;
	background: var(--el-fill-color-darker);
	color: var(--el-text-color-secondary);
	font-weight: 500;
}
.wf-picker__nav-item.active .wf-picker__nav-count {
	background: var(--el-color-primary);
	color: #fff;
}

/* 右侧主体 */
.wf-picker__body {
	flex: 1;
	overflow-y: auto;
	padding: 18px 24px 24px;
	scroll-behavior: smooth;
}
.wf-picker__group + .wf-picker__group {
	margin-top: 22px;
}
.wf-picker__group-header {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 12px;
	padding-bottom: 8px;
}
.wf-picker__group-icon {
	color: var(--el-color-primary);
	font-size: 16px;
}
.wf-picker__group-title {
	font-size: 14px;
	font-weight: 600;
	color: var(--el-text-color-primary);
	margin: 0;
	letter-spacing: 0.4px;
}
.wf-picker__group-divider {
	flex: 1;
	height: 1px;
	background: linear-gradient(
		to right,
		var(--el-border-color-lighter),
		transparent
	);
}
.wf-picker__group-meta {
	font-size: 12px;
	color: var(--el-text-color-secondary);
}

/* 卡片网格 */
.wf-picker__grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
	gap: 10px;
}
.wf-picker__card {
	all: unset;
	box-sizing: border-box;
	cursor: pointer;
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 14px;
	border-radius: 10px;
	background: var(--el-bg-color);
	border: 1px solid var(--el-border-color-lighter);
	transition:
		border-color 0.18s ease,
		box-shadow 0.18s ease,
		transform 0.18s ease,
		background-color 0.18s ease;
	position: relative;
	overflow: hidden;
}
.wf-picker__card::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0;
	bottom: 0;
	width: 3px;
	background: var(--el-color-primary);
	transform: scaleY(0);
	transform-origin: center;
	transition: transform 0.2s ease;
}
.wf-picker__card:hover {
	border-color: var(--el-color-primary-light-5);
	box-shadow: 0 6px 16px -10px var(--el-color-primary);
	transform: translateY(-1px);
	background: var(--el-color-primary-light-9);
}
.wf-picker__card:hover::before {
	transform: scaleY(1);
}
.wf-picker__card:active {
	transform: translateY(0);
}
.wf-picker__card-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	flex-shrink: 0;
	border-radius: 8px;
	background: var(--el-color-primary-light-9);
	color: var(--el-color-primary);
	font-size: 16px;
	transition: background-color 0.18s ease, color 0.18s ease;
}
.wf-picker__card:hover .wf-picker__card-icon {
	background: var(--el-color-primary);
	color: #fff;
}
.wf-picker__card-name {
	flex: 1;
	font-size: 13.5px;
	color: var(--el-text-color-primary);
	font-weight: 500;
	line-height: 1.4;
	text-align: left;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.wf-picker__card-arrow {
	color: var(--el-text-color-placeholder);
	font-size: 14px;
	opacity: 0;
	transform: translateX(-4px);
	transition: opacity 0.18s ease, transform 0.18s ease, color 0.18s ease;
}
.wf-picker__card:hover .wf-picker__card-arrow {
	color: var(--el-color-primary);
	opacity: 1;
	transform: translateX(0);
}

/* 响应式 */
@media (max-width: 768px) {
	:deep(.wf-picker-dialog) {
		--el-dialog-width: 92vw !important;
	}
	.wf-picker {
		height: 72vh;
	}
	.wf-picker__aside {
		width: 140px;
	}
	.wf-picker__nav-text {
		font-size: 13px;
	}
	.wf-picker__body {
		padding: 14px 14px 20px;
	}
}
</style>
