Browse Source

Merge remote-tracking branch 'origin/master'

lanceJiang 4 months ago
parent
commit
fba2243e81

+ 38 - 0
README.md

@@ -45,3 +45,41 @@ cursor-pointer: 鼠标呈现手指样式
 <el-button plain :icon="Delete" :disabled="curSelectionRows.length === 0" @click="batch_del" type="danger">删除</el-button>
 
 ```
+
+
+### 常见问题
+1、如何运行项目
+ - 方案一
+	```bash
+	
+	# 切换镜像源
+	npm config set registry https://mirrors.huaweicloud.com/repository/npm/
+	
+	# 安装yarn
+	npm install -g yarn
+	
+	# 安装依赖
+	yarn
+	
+	# 运行项目
+	yarn dev
+	
+	```
+- 方案二
+  ```
+		# 有梯子,开梯子
+  
+  # 切换镜像源
+	npm config set registry https://registry.npmjs.org/ 和上面那个源,任选其一
+  
+  # 安装yarn
+	npm install -g yarn
+	
+	# 安装依赖
+	yarn
+	
+	# 运行项目
+	yarn dev
+  
+	```
+  

+ 53 - 3
src/api/system/resource.ts

@@ -1,15 +1,20 @@
 import request from '@/utils/request'
 import { AxiosPromise } from 'axios'
 
-// apiUrl 菜单管理
+// apiUrl 菜单、接口管理
 const api = {
 	page: '/sys/resource/page',
 	listTree: '/sys/resource/list-tree',
 	listApi: '/sys/resource/list-api',
 	create: '/sys/resource/create',
 	update: '/sys/resource/update',
-	delete: '/sys/resource/delete'
+	delete: '/sys/resource/delete',
+	sysSourceUpdate: '/sys/resource-api/update', // 根据 id 修改信息
+	sysSourceDelete: '/sys/resource-api/delete', // 根据 ids 删除
+	sysSourceCreate: '/sys/resource-api/create', // 创建
+	sysSourceGet: '/sys/resource-api/get' // 查询id信息
 }
+
 /**
  * 菜单管理 - 列表
  */
@@ -58,10 +63,55 @@ function resourceDeleteApi(data: any): AxiosPromise {
 		data
 	})
 }
+
+function sysSourceUpdateApi(data: any): AxiosPromise {
+	/**
+	 * {
+  "id": 0,
+  "resourceId": 0,
+  "code": "string",
+  "remark": "string"
+}
+	 */
+	return request({
+		url: api.sysSourceUpdate,
+		method: 'post',
+		data
+	})
+}
+
+function sysSourceDeleteApi(data: any): AxiosPromise {
+	return request({
+		url: api.sysSourceDelete,
+		method: 'post',
+		data
+	})
+}
+
+function sysSourceCreateApi(data: any): AxiosPromise {
+	return request({
+		url: api.sysSourceCreate,
+		method: 'post',
+		data
+	})
+}
+
+function sysSourceGetApi(params: any): AxiosPromise {
+	return request({
+		url: api.sysSourceGet,
+		method: 'get',
+		params
+	})
+}
+
 export default {
 	resourcePageApi,
 	resourceListTreeApi,
 	resourceListApi,
 	resourceAddOrEditSaveApi,
-	resourceDeleteApi
+	resourceDeleteApi,
+	sysSourceUpdateApi,
+	sysSourceDeleteApi,
+	sysSourceCreateApi,
+	sysSourceGetApi
 }

+ 7 - 8
src/components/scFormTable/index.vue

@@ -1,15 +1,10 @@
 <template>
 	<div ref="scFormTableRef" class="sc-form-table">
 		<el-table ref="tableRef" :data="data" border stripe>
-			<el-table-column type="index" width="50" fixed="left">
-				<template #header>
-					<el-button v-if="!hideAdd" type="primary" :icon="Plus" size="small" circle @click="rowAdd"></el-button>
-				</template>
+			<el-table-column type="index" width="65" fixed="left">
+				<template #header>序号</template>
 				<template #default="scope">
-					<div :class="['sc-form-table-handle', { 'sc-form-table-handle-delete': !hideDelete }]">
-						<span>{{ scope.$index + 1 }}</span>
-						<el-button v-if="!hideDelete" type="danger" :icon="Delete" size="small" plain circle @click="rowDel(scope.row, scope.$index)"></el-button>
-					</div>
+					<span class="text-center">{{ scope.$index + 1 }}</span>
 				</template>
 			</el-table-column>
 			<el-table-column v-if="dragSort" label="" width="50">
@@ -106,6 +101,10 @@ onMounted(() => {
 		rowDrop()
 	}
 })
+
+defineExpose({
+	rowAdd
+})
 </script>
 
 <style scoped>

+ 1 - 1
src/router/index.ts

@@ -103,7 +103,7 @@ export const sysStaticRouter: Array<AppRouteRecordRaw> = [
 			{
 				path: 'index',
 				component: () => import('@/views/dashboard/productAuth/index.vue'),
-				name: 'product_auth',
+				name: 'product_auth_index',
 				meta: { hidden: true, title: '采购授权', icon: '' }
 			}
 		]

+ 17 - 13
src/views/approve/components/consentOrRefuseDialog.vue

@@ -27,6 +27,7 @@
 				<el-timeline>
 					<el-timeline-item v-for="(item, index) in nodeModelsData" :key="index" placement="top" :timestamp="item.nodeName">
 						<div class="flex">
+							<!-- type: 1[审核人]   2[抄送人]   4[条件路由]   5[子流程]  6[延时处理] 7[触发器]   8[并行路由]  9[包容路由]  10[路由分支]-->
 							<template v-if="item.type === 1">
 								<div v-if="[2, 5].indexOf(item.setType) > -1">
 									<el-tag v-if="item.setType === 5" type="primary">{{ setTypeOptions_config[item.setType] }}</el-tag>
@@ -34,8 +35,10 @@
 										item.examineLevel === 1 ? '直接主管' : `发起人的第${item.examineLevel}级主管`
 									}}</el-tag>
 								</div>
+
 								<template v-if="item.setType === 4">
-									<div v-if="item.selectMode === 3" class="mr-2">
+									<!-- item.nodeCandidate里面的type属性  type:1|0  角色|用户-->
+									<div v-if="item.nodeCandidate.type === 1" class="mr-2">
 										<el-tooltip content="添加角色" placement="bottom">
 											<el-button style="width: 32px" @click="selectHandler(item, 3)">
 												<svg-icon style="font-size: 18px" icon-class="flow-group-add" />
@@ -43,7 +46,7 @@
 										</el-tooltip>
 									</div>
 
-									<div v-else class="mr-2">
+									<div v-if="item.nodeCandidate.type === 0" class="mr-2">
 										<el-tooltip content="添加用户" placement="bottom">
 											<el-button style="width: 32px" @click="selectHandler(item, 1)">
 												<svg-icon style="font-size: 18px" icon-class="flow-user-add" />
@@ -54,10 +57,12 @@
 							</template>
 
 							<template v-else>{{ item.nodeName }}</template>
-							<div v-if="item.selectMode === 0" class="flex flex-wrap gap-[6px]">
+
+							<div v-if="item.selectMode === 1" class="flex flex-wrap gap-[6px]">
 								<FlowNodeAvatar v-for="(childItem, childIndex) in item.nodeAssigneeList" :key="childIndex" :name="childItem.name" />
 							</div>
-							<div v-if="item.selectMode === 1" class="flex flex-wrap gap-[6px]">
+
+							<div v-if="item.selectMode !== 1" class="flex flex-wrap gap-[6px]">
 								<FlowNodeAvatar v-for="(childItem, childIndex) in item.nodeAssigneeList" :key="childIndex" :name="childItem.name">
 									<template #avatar>
 										<svg-icon icon-class="flow-group" color="#fff" />
@@ -103,7 +108,7 @@
 </template>
 
 <script setup>
-import { computed, reactive, watch, toRefs } from 'vue'
+import { computed, reactive, watch, toRefs, nextTick } from 'vue'
 import FileUpload from '@/components/FileUpload.vue'
 import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
 import { nextNodesApi, processConsentTaskApi, processPreviousNodeNameApi, processRejectionTaskApi } from '@/api/flow/processTask'
@@ -158,10 +163,9 @@ const datas = reactive({
 	nodeModelsData: [],
 	active_selectOpts: {},
 	useSelectRef: null,
-	active_assigneeKey: null,
-	nodeTypeData: null
+	active_assigneeKey: null
 })
-const { formRef, uploadLoading, rollbackOptions, btnDisabled, nodeModelsData, active_selectOpts, useSelectRef, active_assigneeKey, nodeTypeData } =
+const { formRef, uploadLoading, rollbackOptions, btnDisabled, nodeModelsData, active_selectOpts, useSelectRef, active_assigneeKey } =
 	toRefs(datas)
 const $myEmit = defineEmits(['update:modelValue', 'successCb'])
 
@@ -195,9 +199,8 @@ const appendToApprovalComments = label => {
 }
 
 const getNextNodesEv = async params => {
-	const { nodeModels, nodeType } = await nextNodesApi(params)
+	const { nodeModels } = await nextNodesApi(params)
 	nodeModelsData.value = nodeModels || []
-	nodeTypeData.value = nodeType
 	/**
 	 * "nodeModels": [
             {
@@ -234,7 +237,6 @@ const updateActive_assigneeMap = assignees => {
 	}
 }
 const selectHandler = (item, type) => {
-	// 发起人自选 (1: 选择一个人, 2: 选择多个人 3: 自选角色)
 	// nodeModelsData.value数组中查找和nodeKey相匹配的对象值
 	const foundNode = findNodeByKey(item.nodeKey)
 
@@ -247,7 +249,9 @@ const selectHandler = (item, type) => {
 
 	active_assigneeKey.value = item.nodeKey
 	active_selectOpts.value = config.selectOpts || {}
-	useSelectRef.value.open(item.selectMode === 3 ? 3 : 1, config.assignees)
+	nextTick(() => {
+		useSelectRef.value.open(type, config.assignees)
+	})
 }
 
 const submitForm = debounce(() => {
@@ -269,7 +273,7 @@ const submitForm = debounce(() => {
 					const validNodes = nodeModelsData.value.filter(node => node.selectMode)
 					if (validNodes.length) {
 						formData.assigneeMap = validNodes.reduce((acc, node) => {
-							acc[node.nodeKey] = { assigneeList: node.nodeAssigneeList, type: nodeTypeData.value }
+							acc[node.nodeKey] = { assigneeList: node.nodeAssigneeList, type: node.nodeCandidate.type }
 							return acc
 						}, {}) // 组装成后台需要的格式
 					} else {

+ 122 - 32
src/views/setting/menu/save.vue

@@ -58,13 +58,16 @@
 							<span class="flex flex-align-pack-center"
 								>路由
 								<el-tooltip content="正常路由地址,次顶级菜单记得'/'开头" placement="right">
-									<Warning style="width: 16px; margin-left: 4px; color: var(--el-color-danger)" /> </el-tooltip></span>
+									<Warning style="width: 16px; margin-left: 4px; color: var(--el-color-danger)" /> </el-tooltip
+							></span>
 						</template>
 						<el-input v-model="form.path" clearable placeholder="请认真填写路由,否则可能无法访问"></el-input>
 					</el-form-item>
 					<el-form-item label="重定向" prop="redirect">
 						<el-input v-model="form.redirect" clearable placeholder=""></el-input>
-						<div class="el-form-item-msg" style="color: var(--el-color-danger)">若为Iframe的外部链接,请填写URL地址,eg: 「https://xxxx.com」格式</div>
+						<div class="el-form-item-msg" style="color: var(--el-color-danger)">
+							若为Iframe的外部链接,请填写URL地址,eg: 「https://xxxx.com」格式
+						</div>
 					</el-form-item>
 					<!--					<el-form-item label="菜单高亮" prop="active">
 						<el-input v-model="form.active" clearable placeholder=""></el-input>
@@ -103,16 +106,42 @@
 			</el-col>
 
 			<el-col :lg="12" class="apilist">
-				<h2>接口权限</h2>
-				<sc-form-table v-model="form.apiList" :add-template="apiListAddTemplate" placeholder="暂无匹配接口权限">
-					<el-table-column prop="code" label="标识" width="150">
+				<div class="flex flex-pack-justify">
+					<h2>接口权限列表</h2>
+					<el-button size="mini" :icon="Plus" @click="addTableTemplate">新增</el-button>
+				</div>
+				<sc-form-table ref="formTable" v-model="form.apiList" :add-template="apiListAddTemplate" placeholder="暂无匹配接口权限">
+					<el-table-column prop="code" label="编码">
+						<template #default="scope">
+							<el-input v-if="scope.row.editing" v-model="scope.row.code" placeholder="请输入编码" :maxlength="100"></el-input>
+							<span v-else>{{ scope.row.code }}</span>
+						</template>
+					</el-table-column>
+					<el-table-column prop="remark" label="备注">
 						<template #default="scope">
-							<el-input v-model="scope.row.code" placeholder="请输入内容"></el-input>
+							<el-input v-if="scope.row.editing" v-model="scope.row.remark" placeholder="请输入备注" :maxlength="255"></el-input>
+							<span v-else>{{ scope.row.remark }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column prop="url" label="Api url">
+					<el-table-column label="操作" width="100">
 						<template #default="scope">
-							<el-input v-model="scope.row.url" placeholder="请输入内容"></el-input>
+							<div v-if="scope.row.editing" class="cursor-pointer">
+								<el-icon @click="saveSourceEv(scope.row, scope.$index)">
+									<Check color="var(--el-color-primary)" />
+								</el-icon>
+							</div>
+							<div v-else>
+								<LeIcon
+									class="text-lg text-icon-color cursor-pointer"
+									icon-class="icon-processInfo-mage--edit"
+									@click="editSourceEv(scope.row, scope.$index)"
+								/>
+								<LeIcon
+									class="text-lg ml-2 text-rose-700 cursor-pointer"
+									icon-class="icon-processInfo-iconoir--trash"
+									@click="deleteSourceEv(scope.row, scope.$index)"
+								/>
+							</div>
 						</template>
 					</el-table-column>
 				</sc-form-table>
@@ -122,31 +151,40 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from 'vue'
-import resource from '@/api/system/resource'
+import { ref, computed, onMounted, toRefs, reactive } from 'vue'
+import { Plus, Check } from '@element-plus/icons-vue'
 import IconPicker from '@/components/IconPicker/index.vue'
 import ScFormTable from '@/components/ScFormTable'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { generateTitle } from '@/utils/i18n'
-const dialogForm = ref()
-const form = ref({
-	id: '',
-	pid: '',
-	alias: '',
-	path: '',
-	component: '',
-	redirect: '',
-	title: '',
-	icon: '',
-	active: '',
-	color: '',
-	type: '0',
-	sort: 0,
-	apiList: [],
-	// 指定父级菜单(路由)名称
-	parentRoute: ''
+import resource from '@/api/system/resource'
+
+const commonObj = reactive({
+	dialogForm: null,
+	formTable: null,
+	form: {
+		id: '',
+		pid: '',
+		alias: '',
+		path: '',
+		component: '',
+		redirect: '',
+		title: '',
+		icon: '',
+		active: '',
+		color: '',
+		type: '0',
+		sort: 0,
+		apiList: [],
+		// 指定父级菜单(路由)名称
+		parentRoute: ''
+	},
+	menuTreeList: null,
+	loading: false,
+	currentResourceId: null
 })
-const menuTreeList = ref()
+const { dialogForm, formTable, menuTreeList, form, loading, currentResourceId } = toRefs(commonObj)
+
 const i18n_formTitle = computed(() => {
 	return generateTitle(form.value.title)
 })
@@ -159,9 +197,9 @@ const rules = {
 }
 const apiListAddTemplate = ref({
 	code: '',
-	url: ''
+	remark: '',
+	editing: true
 })
-const loading = ref(false)
 const $myEmit = defineEmits(['successCb'])
 
 // methods
@@ -187,8 +225,9 @@ const save = () =>
 const setData = async data => {
 	//表单注入数据
 	loading.value = true
+	currentResourceId.value = data.id
 	try {
-		let res = await resource.resourceListApi({ id: data.id })
+		let res = await resource.sysSourceGetApi({ id: data.id })
 		loading.value = false
 		form.value = {
 			...data,
@@ -207,6 +246,57 @@ const setData = async data => {
 	menuTreeList.value = await resource.resourceListTreeApi()
 }
 
+const addTableTemplate = () => {
+	formTable.value.rowAdd()
+}
+
+const saveSourceEv = async (item, idx) => {
+	// 校验 form.apiList 数组中 code 是否都填写
+	if (!item.code) {
+		ElMessage.warning('接口权限的编码不能为空!')
+		return // 中断执行
+	}
+	if (item.id) {
+		await resource.sysSourceCreateApi({ ...item, resourceId: currentResourceId.value })
+		ElMessage.success('保存成功')
+		// let res = await resource.sysSourceGetApi({ id: data.id })
+		// form.value.apiList = res
+		form.value.apiList.splice(idx, 1, {
+			...item,
+			id: Math.random(100), // 写死假的
+			editing: false
+		})
+	} else {
+		await resource.sysSourceUpdateApi({ ...item, resourceId: currentResourceId.value })
+		ElMessage.success('修改成功')
+		// let res = await resource.sysSourceGetApi({ id: data.id })
+		// form.value.apiList = res
+		form.value.apiList.splice(idx, 1, {
+			...item,
+			editing: false
+		})
+	}
+}
+
+const editSourceEv = (item, idx) => {
+	form.value.apiList.splice(idx, 1, {
+		...item,
+		editing: true
+	})
+}
+
+const deleteSourceEv = async (item, idx) => {
+	ElMessageBox.confirm(`确认删除「${item.code}」这条数据?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'error'
+	}).then(async () => {
+		await resource.sysSourceDeleteApi([item.id])
+		ElMessage.success('删除成功')
+		form.value.apiList.splice(idx, 1)
+	})
+}
+
 // 父组件使用的话需要导出
 defineExpose({
 	setData