|
@@ -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>
|