浏览代码

feat: 选择角色 弹窗

lanceJiang 11 月之前
父节点
当前提交
75f1978ce9
共有 1 个文件被更改,包括 248 次插入69 次删除
  1. 248 69
      src/components/SelectRole.vue

+ 248 - 69
src/components/SelectRole.vue

@@ -1,76 +1,255 @@
-<script lang="tsx">
-import { defineComponent, unref, ref } from 'vue'
-import { useI18n } from 'vue-i18n'
-import { useFormSize } from 'element-plus'
+<template>
+	<div class="le-select-role">
+		<el-input :model-value="modelValue" style="display: none" />
+		<el-tooltip v-if="!disabled" content="添加角色" placement="left">
+			<el-button style="width: 32px" @click="selectHandler">
+				<svg-icon style="font-size: 18px" icon-class="flow-group-add" />
+			</el-button>
+		</el-tooltip>
+		<FlowNodeAvatar v-for="(item, i) of modelValue" :key="i" :name="item.name">
+			<template #avatar>
+				<svg-icon icon-class="flow-group" color="#fff" />
+			</template>
+		</FlowNodeAvatar>
+		<el-dialog v-model="dialogVisible" class="le-dialog" title="角色选择" :width="460" destroy-on-close append-to-body>
+			<div class="sc-user-select sc-user-select-role">
+				<div class="sc-user-select__left">
+					<div class="sc-user-select__select">
+						<div v-loading="dataInfo.loading" class="sc-user-select__tree">
+							<el-scrollbar>
+								<el-tree
+									ref="treeRef"
+									class="menu"
+									:data="dataInfo.list"
+									:node-key="roleProps.key"
+									:props="roleProps"
+									show-checkbox
+									check-strictly
+									check-on-click-node
+									:expand-on-click-node="false"
+									:default-checked-keys="selectedIds"
+									@check-change="checkChange"
+								/>
+							</el-scrollbar>
+						</div>
+					</div>
+				</div>
+				<div class="sc-user-select__toicon">
+					<el-icon><ArrowRight /></el-icon>
+				</div>
+				<div class="sc-user-select__selected">
+					<header>已选 ({{ selected.length }})</header>
+					<ul style="padding: 0; margin: 0">
+						<el-scrollbar>
+							<li v-for="(item, index) in selected" :key="item.id">
+								<span class="name">
+									<label>{{ item.name || '-' }}</label>
+								</span>
+								<span class="delete">
+									<el-button type="danger" icon="Delete" circle size="small" @click="removeItem(index)" />
+								</span>
+							</li>
+						</el-scrollbar>
+					</ul>
+				</div>
+			</div>
+			<template #footer>
+				<el-button @click="dialogVisible = false">取消</el-button>
+				<el-button type="primary" @click="submit">确认</el-button>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+<script lang="tsx" setup>
+import { watch, unref, ref, computed, PropType } from 'vue'
 import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
-const Props = {
-	modelValue: {},
-	params: {
-		type: Object
+import role from '@/api/system/role.ts'
+import { ElMessage } from 'element-plus'
+
+defineOptions({ name: 'LeSelectRole' })
+const emit = defineEmits(['update:modelValue', 'change', 'input', 'update:visible', 'getCurRow'])
+type Item = { name: string; id: string; [key: string]: any }
+const props = defineProps({
+	modelValue: {
+		type: Array as PropType<Item[]>,
+		default: () => []
+	},
+	min: { type: Number, default: 0 },
+	max: { type: Number, default: 999999 },
+	disabled: {
+		type: Boolean,
+		default: false
+	},
+	// params: {
+	// 	type: Object
+	// }
+})
+const treeRef = ref()
+const dialogVisible = ref(false)
+const dataInfo = ref({ list: [], loading: false })
+const roleProps = {
+	key: 'id',
+	label: 'name',
+	children: 'children'
+}
+
+// 命中的数据
+const selected = ref<Item[]>([])
+const selectedIds = computed(() => {
+	return selected.value.map(v => v.id)
+})
+watch(
+	() => props.modelValue,
+	vals => {
+		selected.value = JSON.parse(JSON.stringify(vals || []))
+	},
+	{
+		immediate: true,
+		deep: true
+	}
+)
+const checkChange = (data, checked) => {
+	console.log(data, checked, 'data, checked')
+	if (checked) {
+		selected.value.push({
+			id: data.id,
+			name: data.name
+		})
+	} else {
+		const idx = selected.value.findIndex(v => v.id === data.id)
+		if (idx >= 0) {
+			selected.value.splice(idx, 1)
+		}
+	}
+}
+const removeItem = (index: number) => {
+	selected.value.splice(index, 1)
+	treeRef.value?.setCheckedKeys(selectedIds.value)
+}
+// const { t } = useI18n()
+const selectHandler = (nodeKey: string, type: 1 | 3) => {
+	console.log(nodeKey, 'nodeKey type', type)
+	dialogVisible.value = true
+	const _info = dataInfo.value
+	if (!_info.loading && !_info.list.length) {
+		queryRoleData()
 	}
-	// 扩展
-	// 1. prefix -> prefixIcon (prefix 已被vue 拦截)
-	// prefixIcon: String, // eg: '测试Prefix'
-	// 2. suffix -> suffixIcon (prefix 已被vue 拦截 与 suffix 对应做处理)
-	// suffixIcon: String, // eg: '测试suffix'
-	// 3. v-slot: { prefix, suffix } or slots: { prefix: render, suffix: render, }
 }
-const Component = defineComponent({
-	name: 'LeSelectRole',
-	props: Props,
-	emits: [
-		'change',
-		// "update:modelValue"
-	],
-	setup(props, ctx) {
-		const { t } = useI18n()
-		const selectHandler = (nodeKey: string, type: 1 | 3) => {
-			console.log(nodeKey, 'nodeKey type', type)
+const queryRoleData = () => {
+	dataInfo.value.loading = true
+	role
+		.rolePageApi({ page: 1, pageSize: 999 })
+		.then((res: any) => {
+			dataInfo.value = {
+				list: res.records || [],
+				loading: false
+			}
+		})
+		.catch(() => {
+			dataInfo.value.loading = false
+		})
+}
+const submit = () => {
+	// props.modelValue
+	const _selected = selected.value
+	if (!Array.isArray(_selected)) return ElMessage.warning('请选择数据')
+	// 最小限制
+	if (_selected.length < props.min) return ElMessage.warning(`选中的数据个数不能小于${props.min}条`)
+	// 最大限制
+	if (_selected.length > props.max) return ElMessage.warning(`选中的数据个数不能大于${props.max}条`)
+	dialogVisible.value = false
+	emit('update:modelValue', _selected)
+	emit('change', _selected)
+	console.log('我submit了...')
+	// emit('input', _selected)
+}
+// const { prefixIcon, suffixIcon, prop, controlsPosition, t_placeholder, size, placeholder, max = 999999999999999, ...local_props } = ctx.attrs
+</script>
+<style scoped lang="scss">
+.le-select-role {
+	display: flex;
+	align-items: center;
+	gap: 6px;
+	flex-wrap: wrap;
+	min-height: 32px;
+}
+.sc-user-select {
+	display: flex;
+	.sc-user-select__left {
+		width: 200px;
+	}
+	.sc-user-select__tree {
+		width: 200px;
+		//height: 300px;
+		//border: none;
+		height: 343px;
+	}
+
+	.sc-user-select__toicon {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		margin: 0 10px;
+	}
+	.sc-user-select__toicon i {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background: #ccc;
+		width: 20px;
+		height: 20px;
+		text-align: center;
+		line-height: 20px;
+		border-radius: 50%;
+		color: #fff;
+	}
+	.sc-user-select__select {
+		display: flex;
+		border: 1px solid var(--el-border-color-light);
+		background: var(--el-color-white);
+	}
+	.sc-user-select__selected {
+		height: 345px;
+		width: 200px;
+		border: 1px solid var(--el-border-color-light);
+		background: var(--el-color-white);
+		header {
+			height: 43px;
+			line-height: 43px;
+			border-bottom: 1px solid var(--el-border-color-light);
+			padding: 0 15px;
+			font-size: 12px;
 		}
-		const assignees = ref([])
-		return () => {
-			const { prefixIcon, suffixIcon, prop, controlsPosition, t_placeholder, size, placeholder, max = 999999999999999, ...local_props } = ctx.attrs
-			// const inputNumberSize = size || unref(useFormSize())
-			// const _prefix = prefixIcon ? <span class="le-addon le-input-number__prefix">{prefixIcon}</span> : ''
-			// const _suffix = suffixIcon ? <span class="le-addon le-input-number__suffix">{suffixIcon}</span> : ''
-			const _placeholder = (t_placeholder ? t(t_placeholder) : placeholder) ?? t('le.el.input.placeholder')
-			const slots = Object.keys(ctx.slots).length ? ctx.slots : local_props.slots || {}
-			const onChange = (...v) => {
-				ctx.emit('change', ...v)
+		ul {
+			height: 300px;
+			overflow: auto;
+		}
+		li {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 5px 5px 5px 15px;
+			height: 38px;
+			.name {
+				display: flex;
+				align-items: center;
+				.el-avatar {
+					background: var(--el-color-primary);
+					margin-right: 10px;
+				}
+				label {
+				}
+			}
+			.delete {
+				display: none;
+			}
+			&:hover {
+				background: var(--el-color-primary-light-9);
+				.delete {
+					display: inline-block;
+				}
 			}
-			// ${_prefix || slots.prefix ? 'el-input-group--prepend le-input-number--prefix' : ''}
-			// ${_suffix || slots.suffix ? 'el-input-group--append le-input-number--suffix' : ''}
-			/*{"id": "1492884198322536450","name": "财务总监"}*/
-			return (
-				<div class={`le-select-role el-input-group`}>
-					{/*slots.prefix ? slots.prefix() : _prefix*/}
-					{/*v-if="!assigneeMap[v.nodeKey].disabled"*/}
-					<el-tooltip content="添加角色" placement="left">
-							<el-button style="width: 32px" onClick={selectHandler.bind(null, 'nodeKey', 3)}>
-							<svg-icon style="font-size: 18px" icon-class="flow-group-add" />
-						</el-button>
-					</el-tooltip>
-					{
-						assignees.value.map((item, i) => {
-							return <FlowNodeAvatar key={i} name={item.name} style="margin-top: 5px">
-								{{avatar: () => <svg-icon icon-class="flow-group" color="#fff" />}}
-							</FlowNodeAvatar>
-						})
-					}
-					{/*<el-input-number
-						max={max}
-						{...local_props}
-						onChange={onChange}
-						size={inputNumberSize}
-						controlsPosition={controlsPosition ?? 'right'}
-						placeholder={_placeholder}
-						model-value={props.modelValue}
-					/>*/}
-					{/*slots.suffix ? slots.suffix() : _suffix*/}
-				</div>
-			)
 		}
 	}
-})
-export default Component
-</script>
+}
+</style>