|
@@ -0,0 +1,431 @@
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted, watch } from 'vue'
|
|
|
+import { showToast } from '@/components/toast'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ // 要回显的分组id
|
|
|
+ echo: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ // 分组列表
|
|
|
+ list: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 属性
|
|
|
+const groupList = ref([])
|
|
|
+const newGroupName = ref('')
|
|
|
+const showAdd = ref(false)
|
|
|
+const isDisabled = ref(false)
|
|
|
+const currentSelect = ref([])
|
|
|
+
|
|
|
+// 冒泡事件名
|
|
|
+const emits = defineEmits(['emitDisabled', 'onChange', 'onAdd', 'onEdit'])
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ initData()
|
|
|
+})
|
|
|
+
|
|
|
+// 监听isDisabled属性
|
|
|
+watch(
|
|
|
+ () => isDisabled.value,
|
|
|
+ (newVal) => {
|
|
|
+ emits('emitDisabled', newVal)
|
|
|
+ }
|
|
|
+)
|
|
|
+// 监听list属性
|
|
|
+watch(
|
|
|
+ () => props.list,
|
|
|
+ (newVal) => {
|
|
|
+ initData()
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+// function inputCountHighLight() {
|
|
|
+// const countElement = document.querySelector('.el-input__count')
|
|
|
+// const countHtml = document.querySelector('.el-input__count > .el-input__count-inner')
|
|
|
+// if(countElement && countHtml) {
|
|
|
+// const str = countHtml.textContent
|
|
|
+// const regex = /(\d+)\/(\d+)/
|
|
|
+// let newStr = str.replace(regex, '<em class="highlight">$1</em>/$2');
|
|
|
+// countHtml.textContent = newStr
|
|
|
+// countElement.appendChild(countHtml)
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+function initData() {
|
|
|
+ if (!props.list || !props.list.length) return
|
|
|
+ const formatList = props.list.map((v) => {
|
|
|
+ return {
|
|
|
+ id: v.id,
|
|
|
+ name: v.s_name || v.name,
|
|
|
+ input: '',
|
|
|
+ showEdit: false,
|
|
|
+ canEdit: v.isPut,
|
|
|
+ checked: currentSelect.value.length
|
|
|
+ ? currentSelect.value.includes(v.id)
|
|
|
+ : v.isSelect
|
|
|
+ }
|
|
|
+ })
|
|
|
+ groupList.value = formatList
|
|
|
+ if (props.echo) {
|
|
|
+ setGroupSelected(props.echo)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 新增分组改变交互方法
|
|
|
+function onAddAction() {
|
|
|
+ showAdd.value = true
|
|
|
+ newGroupName.value = ''
|
|
|
+ isDisabled.value = true
|
|
|
+}
|
|
|
+// 新增分组确认方法
|
|
|
+function onAddConfirm() {
|
|
|
+ if (!newGroupName.value) {
|
|
|
+ return showToast('请输入分组名称')
|
|
|
+ }
|
|
|
+ if (groupList.value.some((v) => v.name === newGroupName.value)) {
|
|
|
+ return showToast('分组名称已存在')
|
|
|
+ }
|
|
|
+ const newItem = {
|
|
|
+ // id: Math.random(),
|
|
|
+ name: newGroupName.value,
|
|
|
+ showEdit: false,
|
|
|
+ checked: false,
|
|
|
+ input: ''
|
|
|
+ }
|
|
|
+ groupList.value.splice(1, 0, newItem)
|
|
|
+ showAdd.value = false
|
|
|
+ isDisabled.value = false
|
|
|
+ emits('add', { name: newGroupName.value })
|
|
|
+}
|
|
|
+// 新增分组取消方法
|
|
|
+function onAddCancel() {
|
|
|
+ newGroupName.value = ''
|
|
|
+ showAdd.value = false
|
|
|
+ isDisabled.value = false
|
|
|
+}
|
|
|
+// 单项编辑分组
|
|
|
+function onEditGroup(item) {
|
|
|
+ item.input = item.name
|
|
|
+ item.showEdit = true
|
|
|
+ isDisabled.value = true
|
|
|
+}
|
|
|
+// 单项编辑输入方法
|
|
|
+function onItemInput(event, item) {
|
|
|
+ // item.input = event
|
|
|
+}
|
|
|
+// 单项编辑确认方法
|
|
|
+function onItemConfirm(item) {
|
|
|
+ const list = groupList.value
|
|
|
+ if (!item.input) {
|
|
|
+ return showToast('请输入分组名称')
|
|
|
+ }
|
|
|
+ if (list.some((v) => v.name === item.input)) {
|
|
|
+ return showToast('分组名称已存在')
|
|
|
+ }
|
|
|
+ item.name = item.input
|
|
|
+ item.showEdit = false
|
|
|
+ isDisabled.value = false
|
|
|
+ emits('edit', { groupId: item.id, name: item.name })
|
|
|
+}
|
|
|
+// 单项编辑取消方法
|
|
|
+function onItemCancel(item) {
|
|
|
+ item.showEdit = false
|
|
|
+ isDisabled.value = false
|
|
|
+}
|
|
|
+// 分组名称选中事件
|
|
|
+function changeGroupState(item) {
|
|
|
+ const list = groupList.value
|
|
|
+ if (item.name === '默认分组') {
|
|
|
+ list.forEach((v) => {
|
|
|
+ v.checked = false
|
|
|
+ })
|
|
|
+ item.checked = true
|
|
|
+ } else {
|
|
|
+ item.checked = !item.checked
|
|
|
+ const allChecked = list.some((s) => {
|
|
|
+ return s.checked
|
|
|
+ })
|
|
|
+ if (allChecked) {
|
|
|
+ list[0].checked = false
|
|
|
+ } else {
|
|
|
+ list[0].checked = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const selected = getSelected()
|
|
|
+ emits('onChange', selected)
|
|
|
+}
|
|
|
+// 获取选中的分组
|
|
|
+function getSelected() {
|
|
|
+ const data = []
|
|
|
+ const lists = groupList.value
|
|
|
+ lists.forEach((v) => {
|
|
|
+ if (v.checked) {
|
|
|
+ data.push(v.id)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ currentSelect.value = data
|
|
|
+ return data.toString()
|
|
|
+}
|
|
|
+// 设置选中的分组
|
|
|
+function setGroupSelected(data) {
|
|
|
+ if (!data) return
|
|
|
+ const list = groupList.value
|
|
|
+ const newData = data.split(',')
|
|
|
+ list.forEach((v) => {
|
|
|
+ v.checked = false
|
|
|
+ newData.forEach((s) => {
|
|
|
+ if (s === v.id) {
|
|
|
+ v.checked = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <div class="monitor-group" :class="{ disabled: isDisabled }">
|
|
|
+ <div class="flex flex-(items-center justify-end) group-add-container">
|
|
|
+ <div
|
|
|
+ class="flex flex-(items-center justify-between) add-container"
|
|
|
+ v-if="showAdd"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="newGroupName"
|
|
|
+ placeholder="请输入分组名称"
|
|
|
+ class="add-input"
|
|
|
+ maxlength="15"
|
|
|
+ show-word-limit
|
|
|
+ @keyup.enter.native="onAddConfirm"
|
|
|
+ />
|
|
|
+ <div class="flex flex-items-center add-action">
|
|
|
+ <span class="add-confirm" @click="onAddConfirm">确定</span>
|
|
|
+ <span class="add-cancel" @click="onAddCancel">取消</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ v-else
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-plus"
|
|
|
+ @click="onAddAction"
|
|
|
+ class="add-btn"
|
|
|
+ >
|
|
|
+ 新增分组
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="group-list-container">
|
|
|
+ <div v-for="(item, key) in groupList" :key="key" class="group-item">
|
|
|
+ <div
|
|
|
+ v-if="!item.showEdit"
|
|
|
+ class="flex flex-(items-center justify-between) item-name-container"
|
|
|
+ :class="{ 'item-name-checked': item.checked }"
|
|
|
+ @click="changeGroupState(item)"
|
|
|
+ >
|
|
|
+ <div class="flex flex-items-center flex-1">
|
|
|
+ <div
|
|
|
+ v-if="item.name === '默认分组'"
|
|
|
+ class="j-radio"
|
|
|
+ :class="item.checked ? 'r-checked' : ''"
|
|
|
+ ></div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="j-checkbox"
|
|
|
+ :class="item.checked ? 'checked' : ''"
|
|
|
+ ></div>
|
|
|
+ <span class="item-name" :data-id="item.id">{{ item.name }}</span>
|
|
|
+ </div>
|
|
|
+ <span
|
|
|
+ class="item-edit"
|
|
|
+ v-if="item.canEdit"
|
|
|
+ @click.stop="onEditGroup(item)"
|
|
|
+ >编辑</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="flex flex-(item-center space-between) item-edit-container"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="item.input"
|
|
|
+ placeholder="请输入分组名称"
|
|
|
+ class="add-input input-bg"
|
|
|
+ maxlength="15"
|
|
|
+ show-word-limit
|
|
|
+ @input="onItemInput($event, item)"
|
|
|
+ @keyup.enter.native="onItemConfirm(item)"
|
|
|
+ />
|
|
|
+ <div class="flex items-center add-action">
|
|
|
+ <span class="add-confirm" @click="onItemConfirm(item)">确定</span>
|
|
|
+ <span class="add-cancel" @click="onItemCancel(item)">取消</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.monitor-group {
|
|
|
+ .j-radio {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-right: 4px;
|
|
|
+ background: url('~@/assets/images/icon/radio_default.png') no-repeat center
|
|
|
+ center;
|
|
|
+ cursor: pointer;
|
|
|
+ background-size: 20px;
|
|
|
+ &.r-checked {
|
|
|
+ background: url('~@/assets/images/icon/radio_checked2.png') no-repeat
|
|
|
+ center center;
|
|
|
+ background-size: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .j-checkbox {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-right: 4px;
|
|
|
+ border: 0;
|
|
|
+ background: url('~@/assets/images/icon/checkbox_default.png') no-repeat
|
|
|
+ center center;
|
|
|
+ cursor: pointer;
|
|
|
+ background-size: 20px;
|
|
|
+ &.checked {
|
|
|
+ border: 0;
|
|
|
+ background: url('~@/assets/images/icon/checkbox_checked.png') no-repeat
|
|
|
+ center center;
|
|
|
+ background-size: 20px;
|
|
|
+ &[disabled] {
|
|
|
+ background: url('~@/assets/images/icon/checkbox_checked_disabled.png')
|
|
|
+ no-repeat;
|
|
|
+ background-size: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .group-add-container {
|
|
|
+ .add-container {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .group-list-container {
|
|
|
+ margin-top: 20px;
|
|
|
+ max-height: 380px;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid rgba(236, 236, 236, 1);
|
|
|
+ border-radius: 4px;
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 3px;
|
|
|
+ }
|
|
|
+ .item-name {
|
|
|
+ margin-left: 4px;
|
|
|
+ line-height: 22px;
|
|
|
+ }
|
|
|
+ .item-edit {
|
|
|
+ display: none;
|
|
|
+ color: $color_main;
|
|
|
+ cursor: pointer;
|
|
|
+ line-height: 22px;
|
|
|
+ }
|
|
|
+ .group-item {
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ background: #eaf8fa;
|
|
|
+ .item-edit {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &:not(:last-child) {
|
|
|
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .item-name-container {
|
|
|
+ padding: 8px 12px 8px 16px;
|
|
|
+ &.item-name-checked {
|
|
|
+ background: #eaf8fa;
|
|
|
+ color: $color_main;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .item-edit-container {
|
|
|
+ padding: 4px 12px 4px 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .add-confirm {
|
|
|
+ margin-left: 12px;
|
|
|
+ color: $color_main;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ .add-cancel {
|
|
|
+ margin-left: 12px;
|
|
|
+ color: #1d1d1d;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ ::v-deep {
|
|
|
+ .add-input {
|
|
|
+ flex: 1;
|
|
|
+ .el-input__inner {
|
|
|
+ padding: 0 12px;
|
|
|
+ height: 30px;
|
|
|
+ line-height: 30px;
|
|
|
+ }
|
|
|
+ &.input-bg {
|
|
|
+ background: #fff;
|
|
|
+ .el-input__inner {
|
|
|
+ background: #eaf8fa;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .add-btn {
|
|
|
+ padding: 2px 0;
|
|
|
+ span {
|
|
|
+ margin-left: 0px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-input__count-inner {
|
|
|
+ background: transparent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &.disabled {
|
|
|
+ .group-list-container {
|
|
|
+ background: #f5f6f7;
|
|
|
+ }
|
|
|
+ .add-btn {
|
|
|
+ color: #999999;
|
|
|
+ }
|
|
|
+ .group-item {
|
|
|
+ &:hover {
|
|
|
+ .item-edit {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .item-edit-container {
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+ .item-name-container,
|
|
|
+ .add-btn {
|
|
|
+ pointer-events: none;
|
|
|
+ cursor: not-allowed;
|
|
|
+ }
|
|
|
+ .item-name {
|
|
|
+ color: #999999;
|
|
|
+ }
|
|
|
+ .j-radio {
|
|
|
+ background-image: url('~@/assets/images/icon/radio_default_disabled.png');
|
|
|
+ &.r-checked {
|
|
|
+ background-image: url('~@/assets/images/icon/radio_checked_disabled2.png');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .j-checkbox {
|
|
|
+ background-image: url('~@/assets/images/icon/checkbox_default_disabled.png');
|
|
|
+ &.checked {
|
|
|
+ background-image: url('~@/assets/images/icon/checkbox_checked_disabled.png');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|