Browse Source

no message

kllay 5 days ago
parent
commit
87021de74f

+ 5 - 2
src/views/system/borrow/approval/index.vue

@@ -4,13 +4,13 @@
 		<fs-crud ref="crudRef" v-bind="crudBinding">
 			</fs-crud>
       <!-- <ItemsList v-if="formData?.items" :itemss="formData.items" /> -->
-			<RefuseNotification/>
+			<!-- <RefuseNotification/> -->
       <SpecialBorrowApp v-if="showSpecialBorrowDialogapp" v-model:visible="showSpecialBorrowDialogapp" :modelValue="borrowForm" @submit="handleBorrowSubmit" />
 	</fs-page>
 </template>
 
 <script lang="ts" setup name="approval">
-import { onMounted,ref,onBeforeUnmount ,provide,watch,nextTick} from 'vue';
+import { onMounted,ref,onBeforeUnmount,onActivated ,provide,watch,nextTick} from 'vue';
 import { useFs , useCrud, useExpose } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './curd';
 import { ElMessage } from 'element-plus';
@@ -41,6 +41,7 @@ const { crudBinding, crudRef, crudExpose ,crudOptions} = useFs({
 
 
 
+
 provide('showRejectDialog', showRejectDialog);
 provide('rejectReason', rejectReason);
 provide('currentFormId', currentFormId);
@@ -86,6 +87,8 @@ async function handleBorrowSubmit(formData: any) {
 
 
 
+
+
 // const formRef = crudExpose.getFormRef();
 
 const formData = ref<any>(null);

+ 1 - 1
src/views/system/borrow/crud.tsx

@@ -31,7 +31,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 			actionbar: {
 				buttons: {
 					add: {
-					show: true,
+					show: false,
 					text: '创建借用单',
 					click: () => {
 						window.dispatchEvent(new CustomEvent('borrow-add'))

+ 44 - 32
src/views/system/borrow/index.vue

@@ -1,6 +1,6 @@
 <template>
-	<!-- <fs-page class="borrow-page">  -->
-		<fs-page> 
+	<fs-page class="borrow-page"> 
+		<!-- <fs-page>  -->
 		<fs-crud ref="crudRef" v-bind="crudBinding" />
 		<BorrowTypeSelect v-model:visible="showBorrowTypeDialog" @select="onBorrowTypeSelected" />
 		<CommonBorrow 
@@ -24,7 +24,7 @@
 	</fs-page>
 </template>
 <script lang="ts" setup name="borrow">
-import { ref, onMounted, onBeforeUnmount,nextTick ,provide } from 'vue';
+import { ref, onMounted,onActivated, onBeforeUnmount,nextTick ,provide } from 'vue';
 import { useFs } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './crud';
 // import { GetPermission } from './api';
@@ -150,7 +150,7 @@ async function handleSettlement(e: CustomEvent) {
 		borrowForm.value = { ...row, mode: 'settlement' };
 	});
 }
-// let observer: MutationObserver | null = null;
+let observer: MutationObserver | null = null;
 
 onMounted(() => {
 	// 设置列权限
@@ -159,9 +159,9 @@ onMounted(() => {
 	// resetCrudOptions(newOptions);
 	// 刷新
 	crudExpose.doRefresh();
-	window.addEventListener('borrow-add', () => {
-		showBorrowTypeDialog.value = true
-	});
+	// window.addEventListener('borrow-add', () => {
+	// 	showBorrowTypeDialog.value = true
+	// });
 	window.addEventListener('borrow-view', handleView);
 	window.addEventListener('borrow-edit', handleEdit);
 	window.addEventListener('borrow-remove', handleRemove);
@@ -170,23 +170,23 @@ onMounted(() => {
 	window.addEventListener('borrow-settlement', handleSettlement);
 
 
-	// const fsActionbar = document.querySelector('.borrow-page .fs-actionbar');
-	// if (!fsActionbar) return;
-	// // 创建借用单按钮
-	// function insertButton() {
-	// 	if (fsActionbar && !fsActionbar.querySelector('.my-create-btn')) {
-	// 		const button = document.createElement('button');
-	// 		button.className = 'el-button el-button--primary el-button--default my-create-btn';
-	// 		button.innerHTML = '创建借用单';
-	// 		button.onclick = () => {
-	// 			showBorrowTypeDialog.value = true;
-	// 		};
-	// 		fsActionbar.append(button);
-	// 	}
-	// }
+	const fsActionbar = document.querySelector('.borrow-page .fs-actionbar');
+	if (!fsActionbar) return;
+	// 创建借用单按钮
+	function insertButton() {
+		if (fsActionbar && !fsActionbar.querySelector('.my-create-btn')) {
+			const button = document.createElement('button');
+			button.className = 'el-button el-button--primary el-button--default my-create-btn';
+			button.innerHTML = '创建借用单';
+			button.onclick = () => {
+				showBorrowTypeDialog.value = true;
+			};
+			fsActionbar.append(button);
+		}
+	}
 
 	// 先尝试插入一次
-	// insertButton();
+	insertButton();
 
 	// 监听 DOM 变化,确保 actionbar 出现时插入按钮
 	// observer = new MutationObserver(() => {
@@ -194,24 +194,36 @@ onMounted(() => {
 	// });
 	// observer.observe(document.body, { childList: true, subtree: true });
 
-	// const observer = new MutationObserver((_m, obs) => {
-	// 	insertButton();
-	// 	// 插入完毕后停掉 observer
-	// 	obs.disconnect();
-	// });
-	// observer.observe(fsActionbar, { childList: true });
+	observer = new MutationObserver((_m, obs) => {
+		insertButton();
+		// 插入完毕后停掉 observer
+		obs.disconnect();
+	});
+	observer.observe(fsActionbar, { childList: true });
 
 
 });
 onBeforeUnmount(() => {
-	// if (observer) {
-	// 	observer.disconnect();
-	// 	observer = null;
-	// }
+	if (observer) {
+		observer.disconnect();
+		observer = null;
+	}
+	// window.removeEventListener('borrow-add', () => {
+	// 	showBorrowTypeDialog.value = false
+	// });
 	window.removeEventListener('borrow-view', handleView);
+	window.removeEventListener('borrow-edit', handleEdit);
+	window.removeEventListener('borrow-remove', handleRemove);
 	window.removeEventListener('borrow-collect', handleCollect);
 	window.removeEventListener('borrow-return', handleReturn);
 	window.removeEventListener('borrow-settlement', handleSettlement);
 });
+
+// onActivated(() => {
+//   crudExpose.doRefresh();
+// });
+
+
+
 </script>
 <style lang="scss" scoped></style>

+ 7 - 4
src/views/system/borrow/workflow/curd.tsx

@@ -42,12 +42,12 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 			actionbar: {
 				buttons: {
 					add: { 
-						show: true,
+						show: false,
 						text: '新建审批流程',
 						click: () => {
 							window.dispatchEvent(new CustomEvent('workflow-add'))
 						}
-					}, // 关闭 fast-crud 自带新增按钮
+					},
 				},
 			},
 			search:{show:true},
@@ -93,8 +93,8 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 				}},
 				workflow_type:{
 					title: '流程类型id',type:'input', column: { show:false,minWidth: 140 },
-					 search: { show: false },
-					 form:{
+						search: { show: false },
+						form:{
 						component: { placeholder: '请输入设备编号' },
 					}
 				},
@@ -128,6 +128,9 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					title: '更新时间',
 					column: { minWidth: 160, formatter: ({ value }: any) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '') },
 				},
+
+
+
 			},
 		},
 	};

+ 60 - 7
src/views/system/borrow/workflow/index.vue

@@ -1,12 +1,12 @@
 <template>
-	<!-- <fs-page class="workflow-page"> -->
-	<fs-page>
+	<fs-page class="workflow-page">
+	<!-- <fs-page> -->
 		<fs-crud ref="crudRef" v-bind="crudBinding" />
 		<WorkflowDialog v-model:visible="dialogVisible" :editData="editData" :mode="dialogMode" @success="onDialogSuccess" />
 	</fs-page>
 </template>
-<script  lang="ts" setup name="workflow">
-import { ref, onMounted, onBeforeUnmount } from 'vue';
+<script  lang="ts" setup>
+import { ref, onMounted, onBeforeUnmount ,onActivated,onDeactivated} from 'vue';
 import { useFs } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './curd';
 import WorkflowDialog from './WorkflowDialog/index.vue';
@@ -15,6 +15,7 @@ const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useF
 const dialogVisible = ref(false);
 const editData = ref(null);
 const dialogMode = ref<'view' | 'edit' | 'add'>('add');
+let observer: MutationObserver | null = null;
 
 function onAdd() {
 	editData.value = null;
@@ -39,16 +40,68 @@ function onEdit(e: Event) {
 	dialogVisible.value = true;
 }
 
+
+
+
+
 onMounted(() => {
 	crudExpose.doRefresh();
-
-	window.addEventListener('workflow-add',onAdd);
+	// window.addEventListener('workflow-add',onAdd);
 	window.addEventListener('workflow-view', onView);
 	window.addEventListener('workflow-edit', onEdit);
+	const fsActionbarfw = document.querySelector('.workflow-page .fs-actionbar');
+	if (!fsActionbarfw) return;
+	// 创建借用单按钮
+	function insertfwButton() {
+		if (fsActionbarfw && !fsActionbarfw.querySelector('.my-create-btn')) {
+			const buttonwf = document.createElement('button');
+			buttonwf.className = 'el-buttonwf el-button el-button--primary el-button--default my-create-btn';
+			buttonwf.innerHTML = '新建审批流程';
+			buttonwf.onclick = onAdd;
+			fsActionbarfw.append(buttonwf);
+		}
+	}
+
+	insertfwButton();
+
+	observer = new MutationObserver((_m, obs) => {
+		insertfwButton();
+		// 插入完毕后停掉 observer
+		obs.disconnect();
+	});
+	observer.observe(fsActionbarfw, { childList: true ,subtree:true});
+
+
+
+
+
+
+
 });
 onBeforeUnmount(() => {
-
+	if (observer) {
+		observer.disconnect();
+		observer = null;
+	}
+	// window.removeEventListener('workflow-add',onAdd);
 	window.removeEventListener('workflow-view', onView);
 	window.removeEventListener('workflow-edit', onEdit);
 });
+
+
+
+// onMounted(async () => {
+// 	// 设置列权限
+// 	// const newOptions = await handleColumnPermission(GetPermission, crudOptions);
+// 	//重置crudBinding
+// 	// resetCrudOptions(newOptions);
+// 	// 刷新
+// 	crudExpose.doRefresh();
+// });
+
 </script>
+
+<style  scoped>
+
+
+</style>

+ 67 - 0
src/views/system/borrow/workflow/index.vue.rej

@@ -0,0 +1,67 @@
+diff a/src/views/system/borrow/workflow/index.vue b/src/views/system/borrow/workflow/index.vue	(rejected hunks)
+@@ -1,20 +1,20 @@
+ <template>
+-	<fs-page class="workflow-page">
++	<!-- <fs-page class="workflow-page"> -->
++	<fs-page>
+ 		<fs-crud ref="crudRef" v-bind="crudBinding" />
+ 		<WorkflowDialog v-model:visible="dialogVisible" :editData="editData" :mode="dialogMode" @success="onDialogSuccess" />
+ 	</fs-page>
+ </template>
+-<script setup lang="ts">
++<script  lang="ts" setup name="workflow">
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
+ import { useFs } from '@fast-crud/fast-crud';
+ import { createCrudOptions } from './curd';
+ import WorkflowDialog from './WorkflowDialog/index.vue';
+ 
+-const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
++const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
+ const dialogVisible = ref(false);
+ const editData = ref(null);
+ const dialogMode = ref<'view' | 'edit' | 'add'>('add');
+-let observer: MutationObserver | null = null;
+ 
+ function onAdd() {
+ 	editData.value = null;
+@@ -41,38 +41,13 @@
+ 
+ onMounted(() => {
+ 	crudExpose.doRefresh();
+-	// 动态追加“新建审批流程”按钮
+-	function insertfwButton() {
+-		const fsActionbarfw= document.querySelector('.workflow-page .fs-actionbar');
+-		if (fsActionbarfw && !fsActionbarfw.querySelector('.my-create-btn')) {
+-			const buttonwf = document.createElement('button');
+-			buttonwf.className = 'el-buttonwf el-button el-button--primary el-button--default my-create-btn';
+-			buttonwf.innerHTML = '新建审批流程';
+-			buttonwf.onclick = onAdd;
+-			fsActionbarfw.append(buttonwf);
+-		}
+-	}
+ 
+-	// 先尝试插入一次
+-	// insertfwButton();
+-
+-	// 监听 DOM 变化,确保 actionbar 出现时插入按钮
+-	observer = new MutationObserver(() => {
+-		setTimeout(() => {
+-		insertfwButton();
+-		}, 30); 
+-
+-		// insertfwButton();
+-	});
+-	observer.observe(document.body, { childList: true, subtree: true });
++	window.addEventListener('workflow-add',onAdd);
+ 	window.addEventListener('workflow-view', onView);
+ 	window.addEventListener('workflow-edit', onEdit);
+ });
+ onBeforeUnmount(() => {
+-	if (observer) {
+-		observer.disconnect();
+-		observer = null;
+-	}
++
+ 	window.removeEventListener('workflow-view', onView);
+ 	window.removeEventListener('workflow-edit', onEdit);
+ });

+ 70 - 0
src/views/system/testings/WorkflowDialog/api.ts

@@ -0,0 +1,70 @@
+import { id } from 'element-plus/es/locale';
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/system/approval/workflow_step/';
+
+export function GetList(query: UserPageQuery) {
+	return request({
+		url: apiPrefix,
+		method: 'get',
+		params: query,
+	});
+}
+
+export function GetObj(id: InfoReq) {
+	return request({
+		url: apiPrefix + id,
+		method: 'get',
+	});
+}
+
+export function AddObj(obj: AddReq) {
+	return request({
+		url: apiPrefix,
+		method: 'post',
+		data: obj,
+	});
+}
+
+export function UpdateObj(obj: EditReq) {
+	return request({
+		url: '/api/system/approval-workflow/' + obj.id + '/',
+		method: 'put',
+		data: obj,
+	});
+}
+
+export function DelObj(id: DelReq) {
+	return request({
+		url: apiPrefix + id + '/',
+		method: 'delete',
+		data: { id },
+	});
+}
+
+export function switchTenant(tenantId: string) {
+	return request({
+		url: '/api/system/tenant/switch/',
+		method: 'post',
+		data: { tenant_id: tenantId },
+	});
+}
+
+export function GetDeviceClassList() {
+	return request({
+		url: "/api/system/device/category/",
+		method: 'get',
+	});
+}
+
+
+
+/* 获取详情 */
+
+export function GetInfo(id: InfoReq) {
+	return request({
+		url: "/api/system/approval/workflow_step/?workflow_id="+id,
+		method: 'get',
+	});
+}

+ 496 - 0
src/views/system/testings/WorkflowDialog/index.vue

@@ -0,0 +1,496 @@
+<template>
+	<el-dialog
+		:title="editData ? (props.mode === 'view' ? '查看审批流程' : '编辑审批流程') : '新建审批流程'"
+		v-model="visible"
+		width="900px"
+		@close="onCancel"
+	>
+		<el-form :model="form" label-width="110px" :rules="rules" ref="formRef" :disabled="props.mode === 'view'">
+			<el-row :gutter="20">
+				<el-col :span="12">
+					<el-form-item label="流程名称" prop="name">
+						<el-input v-model="form.name" placeholder="请输入" />
+					</el-form-item>
+				</el-col>
+				<!-- <el-col :span="12">
+					<el-form-item label="适用场景" prop="scene">
+						<el-input v-model="form.scene" placeholder="请输入" />
+					</el-form-item>
+				</el-col> -->
+				<el-col :span="12">
+					<el-form-item label="流程类型" prop="workflow_type">
+						<el-select v-model="form.workflow_type" placeholder="请选择流程类型" :disabled="props.mode === 'view'">
+							<el-option label="设备借用申请" value="0" />
+							<el-option label="设备维修申请" value="1" />
+							<el-option label="设备采购申请" value="2" />
+							<el-option label="其他申请" value="3" />
+							<el-option label="课堂借用申请" value="5" />
+						</el-select>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<el-row :gutter="20">
+				
+				<el-col :span="12">
+					<el-form-item label="流程描述" prop="description">
+						<el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入流程描述" :disabled="props.mode === 'view'" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="是否启用" prop="is_active">
+						<el-select v-model="form.is_active" placeholder="请选择启用还是禁用" :disabled="props.mode === 'view'">
+							<el-option label="启用" :value="true" />
+							<el-option label="禁用" :value="false" />
+						</el-select>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<el-divider>条件选择</el-divider>
+			<el-row :gutter="20">
+				<el-col :span="8">
+					<el-form-item label="借用天数大于" prop="condition.days">
+						<el-input-number v-model="form.condition.days" :min="1" controls-position="right" style="width: 100px" />
+						<span style="margin-left: 8px">天</span>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="用户角色" prop="condition.roles">
+						<el-checkbox-group v-model="form.condition.roles">
+							<el-checkbox label="教师" value="teacher"  />
+							<el-checkbox label="学生" value="student" />
+							<el-checkbox label="非本院师生" value="external"  />
+						</el-checkbox-group>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="设备标签" prop="condition.equipment">
+						<el-select v-model="form.condition.equipment" placeholder="请选择">
+						<el-option 
+							v-for="(item, index) in resdevicelist" 
+							:key="index"
+							:label="item.name" 
+							:value="item.id"
+						/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="借用类型" prop="condition.borrow_types">
+						<el-checkbox-group v-model="form.condition.borrow_types" :disabled="props.mode === 'view'">
+							<el-checkbox label="常规借用" value="0"></el-checkbox>
+							<el-checkbox label="课堂借用" value="1"></el-checkbox>
+							<el-checkbox label="特殊借用" value="2"></el-checkbox>
+						</el-checkbox-group>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="借用方式" prop="condition.team_type">
+						<el-checkbox-group v-model="form.condition.team_type" :disabled="props.mode === 'view'">
+							<el-checkbox label="个人借用" value="0"></el-checkbox>
+							<el-checkbox label="团队借用" value="1"></el-checkbox>
+						</el-checkbox-group>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<el-divider>流程配置</el-divider>
+			<div class="workflow-steps">
+				<!-- 借用人 -->
+				<div class="workflow-step">
+					<el-card shadow="never" class="step-card borrower">
+						<div class="step-title">借用人</div>
+					</el-card>
+					<div class="step-line"></div>
+				</div>
+				<!-- 审批步骤 -->
+				<div class="workflow-step" v-for="(step, idx) in form.steps" :key="idx">
+					<el-card shadow="never" class="step-card">
+						<div class="step-title">
+							审批步骤{{ idx + 1 }}
+							<el-button v-if="form.steps.length > 1 && idx !== 0" type="danger" size="small" @click="removeStep(idx)" style="margin-left: 10px">
+								<el-icon><Minus /></el-icon> 删除步骤
+							</el-button>
+						</div>
+						<div class="step-roles">
+							<div v-for="(role, ridx) in step.roles" :key="ridx" class="role-row" style="position: relative; padding: 0 20px">
+								<el-form-item
+									:label="'角色'"
+									:label-position="'left'"
+									label-width="100px"
+									:prop="`steps.${idx}.roles.${ridx}.name`"
+									:rules="[{ required: true, message: '请选择角色', trigger: 'change' }]"
+								>
+									<!-- <el-select v-model="role.name" placeholder="请选择角色" style="width: 300px" @change="onRoleChange(idx, ridx, $event)"> -->
+										<el-select v-model="role.name" placeholder="请选择角色" style="width: 300px">
+										<el-option label="老师" value="老师" />
+										<el-option label="管理员" value="管理员" />
+										<el-option label="学院领导" value="学院领导" />
+									</el-select>
+								</el-form-item>
+								<el-form-item
+									:label="'通过条件'"
+									:label-position="'left'"
+									:prop="`steps.${idx}.roles.${ridx}.passType`"
+									:rules="[{ required: true, message: '请选择通过条件', trigger: 'change' }]"
+								>
+									<el-radio-group v-model="role.passType">
+										<!-- <el-radio label="单人审核"  value="0"></el-radio> -->
+										<el-radio label="1名通过即可"  value="1"></el-radio>
+										<el-radio label="所有人员通过"  value="2"></el-radio>
+										<el-radio label="部门负责人审核"  value="3"></el-radio>
+										<el-radio label="角色审核"  value="4"></el-radio>
+										<el-radio label="自动审核"  value="5"></el-radio>
+									</el-radio-group>
+								</el-form-item>
+								<el-button
+									
+									v-if="step.roles.length > 1 && ridx !== 0"
+									type="danger"
+									size="small"
+									@click="removeRole(idx, ridx)"
+									style="position: absolute; right: 0; top: 0"
+								>
+									<el-icon><Minus /></el-icon> 删除角色
+								</el-button>
+							</div>
+							<div class="addRole" style="width: 100%; display: flex; justify-content: center">
+								<el-button type="primary" size="small" @click="addRole(idx)"  :disabled="step.roles.length >= 5" v-if="props.mode !== 'view'">
+									<el-icon><Plus /></el-icon> 添加角色
+								</el-button>
+							</div>
+						</div>
+					</el-card>
+					<div class="step-line"></div>
+				</div>
+				<!-- 添加步骤按钮 -->
+				<div style="text-align: center; margin: 16px 0">
+					<el-button type="primary" @click="addStep" :disabled="form.steps.length >= 5" v-if="props.mode !== 'view'">
+						<el-icon><Plus /></el-icon> 添加审批步骤
+					</el-button>
+				</div>
+				<!-- 审批完成 -->
+				<div class="workflow-step">
+					<el-card shadow="never" class="step-card done">
+						<div class="step-title">审批完成</div>
+					</el-card>
+				</div>
+			</div>
+		</el-form>
+		<template #footer>
+			<el-button @click="onCancel">取消</el-button>
+			<el-button v-if="props.mode !== 'view'" type="primary" @click="onSave">保存</el-button>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, reactive ,nextTick } from 'vue';
+import { ElMessage } from 'element-plus';
+import { Plus, Minus } from '@element-plus/icons-vue';
+import * as api from '../api';
+import * as firstapi from './api';
+
+
+const props = defineProps<{
+	visible: boolean;
+	editData?: any;
+	mode?: 'view' | 'edit' | 'add';
+}>();
+const emit = defineEmits(['update:visible', 'success']);
+
+const visible = ref(props.visible);
+watch(
+	() => props.visible,
+	(v) => (visible.value = v)
+);
+watch(visible, (v) => emit('update:visible', v));
+
+
+
+
+
+const resdevicelist=reactive([]);
+firstapi.GetDeviceClassList()
+  .then(result => {
+		const deviceData = result.data; 
+		// eslint-disable-next-line no-console
+		console.log("获取到的data值:", deviceData);
+		// 清空并填充响应式数组
+		resdevicelist.splice(0, resdevicelist.length, ...deviceData);
+	})
+	.catch(error => {
+		// eslint-disable-next-line no-console
+		console.error("请求失败:", error);
+});
+
+const defaultRole = () => ({
+	name: '',
+	passType: '',
+});
+const defaultStep = () => ({
+	roles: [defaultRole()],
+});
+
+const form = reactive({
+	name: '',
+	scene: '',
+	workflow_type: "0", 
+	description: '', 
+	is_active: true,
+	condition: {
+		days: 1,
+		roles: [],
+		equipment: [],
+		borrow_types: [], 
+		team_type:[],
+	},
+	steps: [defaultStep()],
+});
+
+const rules = reactive({
+	name: [{ required: true, message: '请输入流程名称', trigger: 'blur' }],
+	workflow_type: [{ required: true, message: '请选择流程类型', trigger: 'change' }],
+	description: [{ required: true, message: '请输入流程描述', trigger: 'blur' }],
+	is_active:[{ required: true, message: '请输选择是否启用', trigger: 'blur' }],
+	'condition.days': [{ required: true, message: '请输入借用天数', trigger: 'change' }],
+	'condition.roles': [{ required: true, message: '请选择用户角色', trigger: 'change' }],
+	'condition.equipment': [{ required: true, message: '请选择设备标签', trigger: 'change' }],
+	'condition.borrow_types': [{ required: true, message: '请选择借用类型', trigger: 'change' }],
+	'condition.team_type': [{ required: true, message: '请选择借用方式', trigger: 'change' }],
+});
+
+const formRef = ref();
+
+
+
+function generateStepRules() {
+  form.steps.forEach((step, stepIdx) => {
+    step.roles.forEach((role, roleIdx) => {
+      rules[`steps.${stepIdx}.roles.${roleIdx}.name`] = [
+        { required: true, message: '请选择角色', trigger: 'change' },
+      ];
+      rules[`steps.${stepIdx}.roles.${roleIdx}.passType`] = [
+        { required: true, message: '请选择通过条件', trigger: 'change' },
+      ];
+    });
+  });
+}
+
+
+const editId=ref('');
+
+watch(
+	() => props.editData,
+	async (val) => {
+		// eslint-disable-next-line no-console
+		console.error("编辑时获取到的值val:", val);
+		if (val) {
+			// Object.assign(form, JSON.parse(JSON.stringify(val)));
+			const cloned = JSON.parse(JSON.stringify(val));
+			form.name = cloned.name || '';
+			form.scene = cloned.scene || '';
+			form.workflow_type = String(cloned.workflow_type ?? '0'); 
+			form.description = cloned.description || '';
+			form.is_active = cloned.is_active ?? true;
+
+			const tc = cloned.trigger_conditions || {};
+			form.condition = {
+				days: 1, 
+				roles: tc.user_types || [],
+				equipment: Array.isArray(tc.equipment_categories) ? tc.equipment_categories[0] : '',
+				borrow_types: tc.borrow_types || [],
+				team_type: tc.team_type || [],
+			};
+
+			// 获取工作流步骤信息
+			const workflowSteps = await firstapi.GetInfo(cloned.id);
+			if (workflowSteps && workflowSteps.data) {
+				editId.value = workflowSteps.data[0].id;
+				form.steps = workflowSteps.data.map((step: any) => ({
+					roles: [{
+						name: step.step_name || '',
+						passType: String(step.step_type ?? '1'),
+					}],
+				}));
+			} else {
+				form.steps = [defaultStep()];
+			}
+
+			generateStepRules();
+		} else {
+			form.name = '';
+			form.scene = '';
+			form.workflow_type = "0";
+			form.description = '';
+			form.is_active = true;
+			form.condition = { days: 1, roles: [], equipment: [], borrow_types: [],team_type:[] };
+			form.steps = [defaultStep()];
+		}
+		generateStepRules();
+
+		await nextTick();
+	},
+	{ immediate: true }
+);
+
+function addStep() {
+	if (form.steps.length >= 5) {
+      ElMessage.warning('最多只能添加 5 个审批步骤');
+      return;
+    }
+	form.steps.push(defaultStep());
+}
+function removeStep(idx: number) {
+	if (form.steps.length > 1) form.steps.splice(idx, 1);
+}
+function addRole(stepIdx: number) {
+	const step = form.steps[stepIdx];
+    if (step.roles.length >= 5) {
+		ElMessage.warning('每个步骤最多只能添加 5 个角色');
+		return;
+    }
+	form.steps[stepIdx].roles.push(defaultRole());
+}
+function removeRole(stepIdx: number, roleIdx: number) {
+	if (form.steps[stepIdx].roles.length > 1) form.steps[stepIdx].roles.splice(roleIdx, 1);
+}
+function onCancel() {
+	visible.value = false;
+}
+
+
+const roleNameToId: { [key: string]: number } = {
+  '管理员': 1,
+  '指导老师': 2
+};
+
+
+async function onSave() {
+
+	try {
+		await formRef.value?.validate();
+		} catch (err) {
+		// eslint-disable-next-line no-console
+		console.warn('表单验证失败:', err);
+		return; 
+		}
+
+	const isEditMode = !!props.editData;
+
+	// 组装接口需要的数据
+	const payload = {
+		name: form.name,
+		workflow_type: form.workflow_type,
+		description: form.description,
+		is_active: typeof form.is_active === 'boolean' ? form.is_active : true,
+		trigger_conditions: {
+			equipment_categories: form.condition?.equipment ? [form.condition.equipment] : ['贵重设备'],
+			borrow_types: form.condition?.borrow_types?.length ? form.condition.borrow_types : ['0', '1', '2'],
+			team_type: form.condition?.team_type?.length ? form.condition.team_type : ['0', '1'],
+			user_types: form.condition?.roles?.length ? form.condition.roles:['student', 'teacher'],
+		},
+	};
+
+	let workflowId = '';
+	// eslint-disable-next-line no-console
+	// console.log(payload);
+	if (isEditMode) {
+		console.log(editId.value);
+		const editRes = await api.UpdateObj(editId.value, payload);
+		workflowId = editId.value;
+		// eslint-disable-next-line no-console
+		console.log(editRes);
+		if (!editRes || editRes.code !== 2000) {
+			ElMessage.error('编辑流程失败');
+			return;
+		}
+	}else{
+		//创建审批
+		const res = await api.AddObj(payload);
+		workflowId = res?.data?.id;
+		if (!workflowId) {
+			ElMessage.error('新增未能获取流程 ID');
+			return;
+		}
+	}	
+
+
+	for (let i = 0; i < form.steps.length; i++) {
+		const step = form.steps[i];
+		const stepOrder = i + 1;
+
+		const roleNames = step.roles.map(role => role.name).filter(Boolean);
+		const passTypes = step.roles.map(role => role.passType).filter(Boolean);
+
+		const stepType = passTypes[0] || '1';
+
+		const approverRoles = roleNames.map(name => roleNameToId[name]).filter(Boolean);
+
+		const stepPayload = {
+			workflow: String(workflowId),
+			step_name: `${roleNames.join(',')}审批`,
+			step_order: stepOrder,
+			step_type: stepType,
+			description: `由${roleNames.join(',')}进行审批`,
+			approvers: [1],  // 可选:你可以根据需要填入具体 ID
+			approver_roles: approverRoles,
+			approver_depts: [],
+			approval_conditions: {
+				required_fields: [],
+				value_ranges: {}
+			},
+			timeout_hours: 24,
+			timeout_action: "auto_reject",
+			is_active: true
+	};
+
+	// eslint-disable-next-line no-console
+	console.log(`步骤 ${stepOrder} payload`, stepPayload);
+		if (isEditMode) {
+			await api.UpdateFirstObj(props.editData.id,stepPayload);
+		}else{
+			await api.AddFirstObj(stepPayload);
+		}
+	
+	}
+
+	ElMessage.success('保存成功');
+	emit('success');
+	visible.value = false;
+}
+
+
+
+
+</script>
+
+<style scoped>
+.workflow-steps {
+	margin: 0 40px;
+}
+.workflow-step {
+	position: relative;
+}
+.step-card {
+	margin-bottom: 0;
+}
+.borrower {
+	background: #e3f0ff;
+}
+.done {
+	background: #eee;
+}
+.step-title {
+	font-weight: bold;
+	font-size: 16px;
+	margin-bottom: 8px;
+}
+.step-line {
+	width: 4px;
+	height: 32px;
+	background: #409eff;
+	margin: 0 auto;
+}
+.role-row {
+	margin-bottom: 8px;
+}
+</style>

+ 84 - 0
src/views/system/testings/api.ts

@@ -0,0 +1,84 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/system/approval/workflow/';
+export const apipoefixsp='/api/system/approval/workflow_step/';
+
+export function GetList(query: UserPageQuery) {
+	return request({
+		url: apiPrefix,
+		method: 'get',
+		params: query,
+	});
+}
+
+export function GetObj(id: InfoReq) {
+	return request({
+		url: apiPrefix + id+ '/',
+		method: 'get',
+	});
+}
+
+export function AddObj(obj: AddReq) {
+	return request({
+		url: apiPrefix,
+		method: 'post',
+		data: obj,
+	});
+}
+
+export function UpdateObj(ids:number,obj: EditReq) {
+	return request({
+		url: '/api/system/approval/workflow_step/' + ids + '/',
+		method: 'put',
+		data: obj,
+	});
+}
+
+export function DelObj(id: DelReq) {
+	return request({
+		url: apiPrefix + id + '/',
+		method: 'delete',
+		data: { id },
+	});
+}
+
+
+
+
+
+export function GetFirstList(query: UserPageQuery) {
+	return request({
+		url: apipoefixsp,
+		method: 'get',
+		params: query,
+	});
+}
+
+
+
+export function AddFirstObj(obj: AddReq) {
+	return request({
+		url: apipoefixsp,
+		method: 'post',
+		data: obj,
+	});
+}
+
+
+export function UpdateFirstObj(ids:number,obj: AddReq) {
+	return request({
+		url: apipoefixsp + ids + '/',
+		method: 'put',
+		data: obj,
+	});
+}
+
+
+export function switchTenant(tenantId: string) {
+	return request({
+		url: '/api/system/tenant/switch/',
+		method: 'post',
+		data: { tenant_id: tenantId },
+	});
+}

+ 137 - 0
src/views/system/testings/curd.tsx

@@ -0,0 +1,137 @@
+import * as api from './api';
+import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import dayjs from 'dayjs';
+
+export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+	const pageRequest = async (query: UserPageQuery) => {
+		try {
+			const res = await api.GetList(query);
+			return {
+				...res,
+				records: res.data,
+				total: res.total,
+			};
+		} catch (error) {
+			return {
+				code: 2000,
+				msg: 'success',
+				records: [],
+				total: 0,
+			};
+		}
+	};
+	const editRequest = async ({ form, row }: EditReq) => {
+		form.id = row.id;
+		return await api.UpdateObj(row.id,form);
+	};
+	const delRequest = async ({ row }: DelReq) => {
+		return await api.DelObj(row.id);
+	};
+	const addRequest = async ({ form }: AddReq) => {
+		return await api.AddObj(form);
+	};
+
+	return {
+		crudOptions: {
+			request: {
+				pageRequest,
+				addRequest,
+				editRequest,
+				delRequest,
+			},
+			actionbar: {
+				buttons: {
+					add: { 
+						show: true,
+						text: '新建审批流程',
+						click: () => {
+							window.dispatchEvent(new CustomEvent('workflow-add'))
+						}
+					},
+				},
+			},
+			search:{show:false},
+			toolbar:{show:false},
+			rowHandle: {
+				fixed: 'right',
+				width: 220,
+				buttons: {
+					view: {
+						show: true,
+						click: ({ row }) => {
+							window.dispatchEvent(new CustomEvent('workflow-view', { detail: row }));
+						},
+					},
+					edit: {
+						show: true,
+						click: ({ row }) => {
+							window.dispatchEvent(new CustomEvent('workflow-edit', { detail: row }));
+						},
+					},
+					remove: {
+						show: true,
+					},
+				},
+			},
+			columns: {
+				_index: {
+					title: '序号',
+					form: { show: false },
+					column: {
+						align: 'center',
+						width: '70px',
+						columnSetDisabled: true,
+						formatter: (context: any) => {
+							let index = context.index ?? 1;
+							let pagination = crudExpose!.crudBinding.value.pagination;
+							return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
+						},
+					},
+				},
+				name: { title: '流程名称',type:'input', column: { minWidth: 140 }, search: { show: true} , form:{
+					component: { placeholder: '请输入设备编号' },
+				}},
+				workflow_type:{
+					title: '流程类型id',type:'input', column: { show:false,minWidth: 140 },
+						search: { show: false },
+						form:{
+						component: { placeholder: '请输入设备编号' },
+					}
+				},
+				"trigger_conditions.team_type":{
+					title: '流程类型id', column: { show:false,minWidth: 140 }, search: { show: false }
+				},
+				"trigger_conditions.user_types":{
+					title: '流程类型id', column: { show:false,minWidth: 140 }, search: { show: false }
+				},
+				"trigger_conditions.borrow_types":{
+					title: '流程类型id', column: { show:false,minWidth: 140 }, search: { show: false }
+				},
+				"trigger_conditions.equipment_categories":{
+					title: '流程类型id', column: { show:false,minWidth: 140 }, search: { show: false }
+				},
+				workflow_type_label: { title: '流程类型', column: { minWidth: 120 } },
+				description: { title: '流程描述', column: { minWidth: 200 } },
+				step_count: { title: '步骤数', column: { minWidth: 80, align: 'center' } },
+				is_active: {
+					title: '状态',
+					column: {
+						minWidth: 80,
+						formatter: ({ value }: any) => (value ? '启用' : '停用'),
+					},
+				},
+				create_datetime: {
+					title: '创建时间',
+					column: { minWidth: 160, formatter: ({ value }: any) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '') },
+				},
+				update_datetime: {
+					title: '更新时间',
+					column: { minWidth: 160, formatter: ({ value }: any) => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '') },
+				},
+
+
+
+			},
+		},
+	};
+};

+ 107 - 0
src/views/system/testings/index.vue

@@ -0,0 +1,107 @@
+<template>
+	<fs-page>
+		<fs-crud ref="crudRef" v-bind="crudBinding" />
+		<!-- <WorkflowDialog v-model:visible="dialogVisible" :editData="editData" :mode="dialogModesss" @success="onDialogSuccess" /> -->
+	</fs-page>
+</template>
+<script  lang="ts" setup>
+import { ref, onMounted, onBeforeUnmount ,onActivated,onDeactivated} from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import { createCrudOptions } from './curd';
+import WorkflowDialog from './WorkflowDialog/index.vue';
+
+const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
+// const dialogVisible = ref(false);
+// const editData = ref(null);
+// const dialogModesss = ref<'view' | 'edit' | 'add'>('add');
+
+
+// function onAdd() {
+// 	editData.value = null;
+// 	dialogModesss.value = 'add';
+// 	dialogVisible.value = true;
+// }
+function onDialogSuccess() {
+	dialogVisible.value = false;
+	crudExpose.doRefresh();
+}
+
+// function onView(e: Event) {
+// 	const ce = e as CustomEvent;
+// 	editData.value = ce.detail;
+// 	dialogModesss.value = 'view';
+// 	dialogVisible.value = true;
+// }
+// function onEdit(e: Event) {
+// 	const ce = e as CustomEvent;
+// 	editData.value = ce.detail;
+// 	dialogModesss.value = 'edit';
+// 	dialogVisible.value = true;
+// }
+
+
+
+
+
+// 1. 激活时:刷新列表 + 绑定事件
+// function activate() {
+//   crudExpose.doRefresh();
+//   console.log("绑定")
+//   window.addEventListener('workflow-add', onAdd);
+//   window.addEventListener('workflow-view', onView);
+//   window.addEventListener('workflow-edit', onEdit);
+// }
+
+// // 2. 停用/销毁时:解绑事件
+// function deactivate() {
+// 	console.log("解绑")
+//   window.removeEventListener('workflow-add', onAdd);
+//   window.removeEventListener('workflow-view', onView);
+//   window.removeEventListener('workflow-edit', onEdit);
+//   console.log("解绑完成")
+// }
+
+// 3. 注册钩子
+// onActivated(activate);
+// onDeactivated(deactivate);
+
+// 4. 首次挂载也要激活一次
+// onMounted(activate);
+
+// // // 5. 组件真正销毁前再解绑,额外保险
+// onBeforeUnmount(deactivate);
+
+// onActivated(activate);
+// onDeactivated(deactivate);
+
+// onMounted(() => {
+// 	crudExpose.doRefresh();
+// 	console.log("流程页面首次!!");
+// 	window.addEventListener('workflow-add',onAdd);
+// 	window.addEventListener('workflow-view', onView);
+// 	window.addEventListener('workflow-edit', onEdit);
+// });
+// onBeforeUnmount(() => {
+// 	console.log("流程页面离开!!");
+// 	window.removeEventListener('workflow-add',onAdd);
+// 	window.removeEventListener('workflow-view', onView);
+// 	window.removeEventListener('workflow-edit', onEdit);
+// });
+
+
+
+onMounted(async () => {
+	// 设置列权限
+	// const newOptions = await handleColumnPermission(GetPermission, crudOptions);
+	//重置crudBinding
+	// resetCrudOptions(newOptions);
+	// 刷新
+	crudExpose.doRefresh();
+});
+
+</script>
+
+<style  scoped>
+
+
+</style>