Pārlūkot izejas kodu

feat: 分类 + 模板 api提交

luoyali 1 gadu atpakaļ
vecāks
revīzija
31c8734b65

+ 68 - 0
src/api/system/formcategory.ts

@@ -0,0 +1,68 @@
+import request from '@/utils/request'
+import { AxiosPromise } from 'axios'
+
+// apiUrl 流程表单分类
+const api = {
+	update: '/v1/form-category/update', // id修改信息
+	page: '/v1/form-category/page', // 分页列表
+	listTree: '/v1/form-category/list-tree', // 树列表
+	delete: '/v1/form-category/delete', // 根据ids删除
+	create: '/v1/form-category/create', // 创建
+	listAll: '/v1/form-category/list-all', // 列表 (所有部门)
+	getOneDetail: '/v1/form-category/get' // 查询id信息
+}
+
+function formCategoryPageApi(data: any): AxiosPromise {
+	return request({
+		url: api.page,
+		method: 'post',
+		data
+	})
+}
+
+function formCategoryAddOrEditSaveApi(data: any): AxiosPromise {
+	return request({
+		url: data.id ? api.update : api.create,
+		method: 'post',
+		data
+	})
+}
+
+function formCategoryListTreeApi(data: any): AxiosPromise {
+	return request({
+		url: api.listTree,
+		method: 'post',
+		data
+	})
+}
+
+function formCategoryDeleteApi(data: any): AxiosPromise {
+	return request({
+		url: api.delete,
+		method: 'post',
+		data
+	})
+}
+
+function formCategoryDetailApi(): AxiosPromise {
+	return request({
+		url: api.getOneDetail,
+		method: 'get'
+	})
+}
+
+function formCategoryListAllApi(): AxiosPromise {
+	return request({
+		url: api.listAll,
+		method: 'get'
+	})
+}
+
+export default {
+	formCategoryPageApi,
+	formCategoryAddOrEditSaveApi,
+	formCategoryDeleteApi,
+	formCategoryDetailApi,
+	formCategoryListAllApi,
+	formCategoryListTreeApi
+}

+ 49 - 0
src/api/system/formtemplate.ts

@@ -0,0 +1,49 @@
+import request from '@/utils/request'
+import { AxiosPromise } from 'axios'
+
+// apiUrl 流程表单分类
+const api = {
+	update: '/v1/form-template/update', // id修改信息
+	page: '/v1/form-template/page', // 分页列表
+	delete: '/v1/form-template/delete', // 根据ids删除
+	create: '/v1/form-template/create', // 创建
+	getOneDetail: '/v1/form-template/get' // 查询id信息
+}
+
+function formTemplatePageApi(data: any): AxiosPromise {
+	return request({
+		url: api.page,
+		method: 'post',
+		data
+	})
+}
+
+function formTemplateAddOrEditSaveApi(data: any): AxiosPromise {
+	return request({
+		url: data.id ? api.update : api.create,
+		method: 'post',
+		data
+	})
+}
+
+function formTemplateDeleteApi(data: any): AxiosPromise {
+	return request({
+		url: api.delete,
+		method: 'post',
+		data
+	})
+}
+
+function formTemplateDetailApi(): AxiosPromise {
+	return request({
+		url: api.getOneDetail,
+		method: 'get'
+	})
+}
+
+export default {
+	formTemplatePageApi,
+	formTemplateAddOrEditSaveApi,
+	formTemplateDeleteApi,
+	formTemplateDetailApi
+}

+ 474 - 0
src/views/setting/form/index.vue

@@ -0,0 +1,474 @@
+<template>
+	<div class="pageWrap bgc">
+		<div class="aside-box">
+			<el-aside width="200px" style="height: 100%">
+				<el-container style="height: 100%">
+					<el-header>
+						<el-input v-model="groupFilterText" placeholder="输入关键字进行过滤" clearable style="margin-top: 10px" />
+					</el-header>
+					<el-main style="flex-basis: 100%; padding: 0px">
+						<el-tree
+							ref="treeRef"
+							class="menu-tree"
+							:data="treeData"
+							node-key="id"
+							:current-node-key="''"
+							:highlight-current="true"
+							:expand-on-click-node="false"
+							:props="{ label: 'name' }"
+							default-expand-all
+							:filter-node-method="filterNode"
+							@node-click="leftTreeClick"
+						>
+							<template #default="{ node, data }">
+								<span class="custom-tree-node">
+									<span class="label">{{ node.label }}</span>
+									<span class="code">{{ data.code }}</span>
+									<span class="do">
+										<el-icon @click.stop="dicEdit(data)"><Edit /></el-icon>
+										<el-icon @click.stop="dictDel(node, data)"><Delete /></el-icon>
+									</span>
+								</span>
+							</template>
+						</el-tree>
+					</el-main>
+					<el-footer style="height: 51px">
+						<el-button type="primary" style="width: 100%" @click="addHandler('tree')">
+							<el-icon>
+								<Plus />
+							</el-icon>
+							表单分类
+						</el-button>
+					</el-footer>
+				</el-container>
+			</el-aside>
+		</div>
+		<div class="content-warp flex-column-page-wrap">
+			<!-- 公用搜索组件 -->
+			<LeSearchForm ref="searchForm" v-model:searchData="searchData" :forms="forms" :loading="tableOpts.options.loading"></LeSearchForm>
+			<!--  LeTable 组件使用  -->
+			<LeTable
+				ref="tableRef"
+				v-model:searchParams="tableOpts.searchParams"
+				v-bind="tableOpts"
+				v-model:curRow="tableOpts.curRow"
+				v-model:checked-options="checkedColumns"
+				:columns="activeColumns"
+			>
+				<template #toolLeft>
+					<el-button type="primary" @click="addHandler('table')">
+						<el-icon class="btn-icon">
+							<Plus />
+						</el-icon>
+					</el-button>
+					<el-button type="danger" :disabled="curSelectionRows.length === 0" @click="batch_del">
+						<el-icon class="btn-icon">
+							<Delete />
+						</el-icon>
+					</el-button>
+				</template>
+
+				<template #filterAvatarSlot="scope">
+					<el-avatar :src="scope.row.avatar" size="small"></el-avatar>
+				</template>
+
+				<template #statusSlot="scope">
+					<status-indicator v-if="scope.row.status === 1" pulse type="success"></status-indicator>
+					<status-indicator v-else pulse type="danger"></status-indicator>
+				</template>
+
+				<template #actionSlot="scope">
+					<el-tooltip content="编辑" placement="bottom" effect="light">
+						<el-icon class="ibt0" @click="table_edit(scope.row)">
+							<Edit />
+						</el-icon>
+					</el-tooltip>
+					<el-divider direction="vertical"></el-divider>
+					<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row)">
+						<template #reference>
+							<el-icon class="ibt0">
+								<Delete />
+							</el-icon>
+						</template>
+					</el-popconfirm>
+				</template>
+			</LeTable>
+		</div>
+
+		<LeFormConfigDialog
+			v-if="visible"
+			ref="dialogDictRef"
+			v-model="visible"
+			:title="`${isCreate ? '新增' : '编辑'}项`"
+			width="600px"
+			:form-data="activeData"
+			:form-options="formOptions"
+			@submit="submitHandler"
+		/>
+	</div>
+</template>
+<script lang="tsx" setup>
+import dict from '@/api/system/dict'
+import { computed, nextTick, ref, watch } from 'vue'
+import { ElMessage, ElTree, ElMessageBox } from 'element-plus'
+import { useTablePage } from '@/hooks/useTablePage'
+import { Plus, Delete, Edit } from '@element-plus/icons-vue'
+import StatusIndicator from '@/components/StatusIndicator'
+
+const visible = ref(false) // 弹窗显示隐藏
+const isCreate = ref(true)
+const activeData = ref({})
+const current_mode = ref('') // 当前模式 (tree | table)
+const formsDialog = [
+	{
+		prop: 'pid',
+		label: '所属字典',
+		itemType: 'select',
+		filterable: true,
+		options: [],
+		rules: [{ required: true, message: '请选择所属字典', trigger: 'blur' }]
+	},
+	{
+		prop: 'name',
+		label: '项名称',
+		itemType: 'input',
+		rules: [{ required: true, message: '请输入项名称', trigger: 'blur' }]
+	},
+	{
+		prop: 'code',
+		label: '编码',
+		itemType: 'input',
+		rules: [{ required: true, message: '请输入编码', trigger: 'blur' }]
+	},
+	{
+		prop: 'status',
+		label: '状态',
+		itemType: 'switch',
+		activeText: '是',
+		inactiveText: '否'
+	},
+	{
+		prop: 'sort',
+		label: '排序',
+		itemType: 'inputNumber',
+		rules: [{ required: true, message: '请输入排序', trigger: 'blur' }]
+	},
+	{
+		prop: 'remark',
+		label: '备注',
+		itemType: 'input'
+	}
+]
+// 新增的表单 和 编辑的表单
+const formOptions = computed(() => {
+	let tempForm = []
+	if (current_mode.value === 'tree') {
+		for (let i in formsDialog) {
+			let fd = formsDialog[i]
+			if (fd.prop == 'pid') {
+				continue
+			}
+			tempForm.push(fd)
+		}
+	} else {
+		tempForm = formsDialog
+	}
+	return {
+		forms: tempForm,
+		labelWidth: 120,
+		span: 30,
+		showResetBtn: true,
+		formConfig: {
+			showCancelBtn: true,
+			submitLoading: false
+		}
+	}
+})
+const showGroupLoading = ref(false)
+const groupFilterText = ref('')
+const treeRef = ref<InstanceType<typeof ElTree>>()
+
+const filterNode = (value: string, data: Tree) => {
+	if (!value) return true
+	return data.name.includes(value)
+}
+
+const treeData = ref([])
+
+// 获取左侧菜单数据
+const getGroup = async () => {
+	showGroupLoading.value = true
+	let data = await dict.dictListParentApi()
+	formOptions.value.forms[0].options = data.map(item => {
+		return { value: item.id, label: item.name }
+	})
+	showGroupLoading.value = false
+	data.unshift({ id: '', name: '所有' })
+	treeData.value = data
+}
+
+// 左侧菜单点击
+const leftTreeClick = data => {
+	searchData.value = { ...searchData.value, pid: data.id ? data.id : null }
+}
+
+// 表格搜索条件
+const forms = ref([
+	{
+		prop: 'name',
+		label: '名称:',
+		itemType: 'input',
+		placeholder: '请输入名称'
+	},
+	{
+		prop: 'code',
+		label: '编码:',
+		itemType: 'input',
+		placeholder: '请输入编码'
+	},
+	{
+		prop: 'status',
+		label: '状态:',
+		itemType: 'select',
+		placeholder: '请选择状态',
+		options: [
+			{
+				label: '禁用',
+				value: 0
+			},
+			{
+				label: '正常',
+				value: 1
+			}
+		]
+	}
+])
+// table列表数据请求
+const queryList = async () => {
+	const { options, searchParams } = tableOpts
+	options.loading = true
+	try {
+		const { records: list, total } = await dict.dictPageApi(searchParams)
+		tableOpts.total = total
+		tableOpts.list = list
+	} catch {
+		tableOpts.total = 0
+		tableOpts.list = []
+	} finally {
+		options.loading = false
+	}
+}
+
+// table 参数
+const columns = [
+	{
+		prop: 'name',
+		label: '名称',
+		minWidth: 80
+	},
+	{
+		prop: 'code',
+		label: '编码',
+		minWidth: 100
+	},
+	{
+		prop: 'status',
+		label: '状态',
+		minWidth: 50,
+		slots: {
+			default: 'statusSlot'
+		}
+	},
+	{
+		prop: 'content',
+		label: '内容',
+		minWidth: 150
+	},
+	{
+		prop: 'remark',
+		label: '备注',
+		minWidth: 100
+	},
+	{
+		prop: 'sort',
+		label: '排序',
+		minWidth: 50
+	},
+	{
+		prop: 'action',
+		label: '操作',
+		width: 100,
+		fixed: 'right',
+		slots: {
+			default: 'actionSlot'
+		}
+	}
+]
+
+const { searchData, tableOpts, checkedColumns, activeColumns, curSelectionRows, updateParams } = useTablePage(
+	{
+		options: {
+			showIndex: false
+		},
+		// 需要展示的列
+		columns,
+		// 控制列配置
+		columnsConfig: {
+			columns
+		}
+	},
+	{
+		queryList,
+		fetchImmediate: false
+	}
+)
+
+// 删除
+const deleteItem = async ids => {
+	// try {
+	await dict.dictDeleteApi(ids)
+	if (current_mode.value === 'tree') {
+		getGroup()
+	}
+	updateParams()
+	ElMessage.success(`删除成功~`)
+	// } catch (e) {
+	// 	ElMessage.error(`删除失败~`)
+	// }
+}
+
+// 单个删除
+const table_del = row => {
+	deleteItem([row.id])
+}
+
+//批量删除
+const batch_del = () => {
+	const ids = curSelectionRows.value.map(item => item.id) // 多选数据
+	ElMessageBox.confirm('是否删除选中数据?', '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'error',
+		buttonSize: 'default'
+	}).then(() => {
+		deleteItem(ids)
+	})
+}
+
+const table_edit = async row => {
+	isCreate.value = false
+	activeData.value = { ...row, status: row.status ? true : false }
+	visible.value = true
+}
+
+// 弹窗事件
+const submitHandler = async params => {
+	try {
+		formOptions.value.formConfig.submitLoading = true
+		params.status = params.status ? 1 : 0
+		params.id = activeData.value.id ? activeData.value.id : null
+		await dict.dictAddOrEditSaveApi(params)
+		ElMessage.success(`${isCreate.value ? '新增' : '修改'}成功~`)
+		visible.value = false
+		if (current_mode.value === 'tree') {
+			getGroup()
+		}
+		updateParams()
+	} finally {
+		formOptions.value.formConfig.submitLoading = false
+	}
+}
+
+const addHandler = mode => {
+	isCreate.value = true
+	activeData.value = {}
+	visible.value = true
+	current_mode.value = mode
+}
+
+// 删除左侧树
+const dictDel = (node, data) => {
+	current_mode.value = 'tree'
+	ElMessageBox.confirm(`确定删除 ${data.name} 项吗?`, '提示')
+		.then(async () => {
+			// try {
+			deleteItem([data.id])
+			// } catch (e) {
+			// 	ElMessage.error(`删除失败~`)
+			// }
+		})
+		.catch(() => {})
+}
+
+const dicEdit = data => {
+	current_mode.value = 'tree'
+	table_edit(data)
+}
+
+nextTick(() => {
+	getGroup()
+	queryList()
+})
+
+watch(groupFilterText, val => {
+	treeRef.value!.filter(val)
+})
+</script>
+<style scoped lang="scss">
+// 表单的树结构样式
+:deep(.menu-tree) {
+	.el-tree-node {
+		&:first-child {
+			.custom-tree-node {
+				.do {
+					display: none !important;
+				}
+			}
+		}
+	}
+}
+
+.custom-tree-node {
+	display: flex;
+	flex: 1;
+	align-items: center;
+	justify-content: space-between;
+	font-size: 14px;
+	padding-right: 24px;
+	height: 100%;
+}
+
+.custom-tree-node .code {
+	font-size: 12px;
+	color: #999;
+}
+
+.custom-tree-node {
+	.do {
+		display: none;
+	}
+}
+
+.custom-tree-node .do i {
+	margin-left: 3px;
+	color: #999;
+	padding: 3px;
+	font-size: 20px;
+}
+
+.custom-tree-node .do i:hover {
+	color: #333;
+}
+
+.custom-tree-node {
+	&:hover {
+		.code {
+			display: none;
+		}
+	}
+}
+
+.custom-tree-node:hover .do {
+	display: inline-block;
+}
+</style>