|
@@ -0,0 +1,250 @@
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ title="表单排序"
|
|
|
+ :visible.sync="visible"
|
|
|
+ width="600px"
|
|
|
+ append-to-body
|
|
|
+ @close="handleClose"
|
|
|
+ class="project-dialog"
|
|
|
+ >
|
|
|
+ <span slot="title">
|
|
|
+ <i class="el-icon-sort" style="color: #2550A2;"></i>{{ title }}
|
|
|
+ </span>
|
|
|
+ <div class="sort-container">
|
|
|
+ <!-- 提示信息 -->
|
|
|
+ <div class="tip-box">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 拖动项目可调整顺序,或者使用箭头按钮、输入序号进行排序
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="sort-list">
|
|
|
+ <!-- 遍历每个分类 -->
|
|
|
+ <div class="category-section" v-for="(category, categoryIndex) in sortList" :key="categoryIndex">
|
|
|
+ <div class="category-header">{{ category.title }}</div>
|
|
|
+
|
|
|
+ <!-- 使用draggable包裹每个分类下的项目列表 -->
|
|
|
+ <draggable
|
|
|
+ v-model="category.list"
|
|
|
+ :options="{ group: `category-${categoryIndex}`, animation: 150 }"
|
|
|
+ @end="handleDragEnd(categoryIndex)"
|
|
|
+ class="draggable-container"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(item, itemIndex) in category.list"
|
|
|
+ :key="item.id"
|
|
|
+ class="sort-item"
|
|
|
+ >
|
|
|
+ <i class="el-icon-rank drag-handle"></i>
|
|
|
+ <div class="item-content">
|
|
|
+ <span>{{ item.tableName }}</span>
|
|
|
+ <div class="item-actions">
|
|
|
+ <div class="move-btns">
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-arrow-up"
|
|
|
+ :disabled="itemIndex === 0"
|
|
|
+ @click="moveItem(categoryIndex, itemIndex, -1)"
|
|
|
+ ></el-button>
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-arrow-down"
|
|
|
+ :disabled="itemIndex === category.list.length - 1"
|
|
|
+ @click="moveItem(categoryIndex, itemIndex, 1)"
|
|
|
+ ></el-button>
|
|
|
+ </div>
|
|
|
+ <span>移至</span>
|
|
|
+ <el-input-number
|
|
|
+ :controls="false"
|
|
|
+ v-model="item.sortNum"
|
|
|
+ :min="1"
|
|
|
+ :max="category.list.length"
|
|
|
+ size="mini"
|
|
|
+ style="width: 60px;"
|
|
|
+ @change="handleNumberChange(categoryIndex, $event, itemIndex)"
|
|
|
+ ></el-input-number>
|
|
|
+ <span>位</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </draggable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer" style="text-align: center">
|
|
|
+ <el-button @click="handleClose">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="handleConfirm" style="background-color: #2550A2;border-color: #2550A2 ;color: white;">确 定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import draggable from 'vuedraggable'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'ContractSort',
|
|
|
+ components: {
|
|
|
+ draggable
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '排序'
|
|
|
+ },
|
|
|
+ sortProLoad: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ sortList: [], // 结构应为 [{ title: '分类1', list: [{ id: 1, tableName: '项目1', sortNum: 1 }] }, ...]
|
|
|
+ visible: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ show(contractList) {
|
|
|
+ if(!contractList || !Array.isArray(contractList)) {
|
|
|
+ console.warn('contractList must be an array');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 初始化排序列表,确保结构是带分类的
|
|
|
+ this.sortList = contractList.map((category, categoryIndex) => ({
|
|
|
+ ...category,
|
|
|
+ list: category.list.map((item, itemIndex) => ({
|
|
|
+ ...item,
|
|
|
+ sortNum: itemIndex + 1
|
|
|
+ }))
|
|
|
+ }));
|
|
|
+ this.visible = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ hide() {
|
|
|
+ this.visible = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 拖动结束后重新计算当前分类的序号
|
|
|
+ handleDragEnd(categoryIndex) {
|
|
|
+ this.sortList[categoryIndex].list.forEach((item, index) => {
|
|
|
+ item.sortNum = index + 1;
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 移动项目(仅在当前分类内)
|
|
|
+ moveItem(categoryIndex, itemIndex, direction) {
|
|
|
+ const category = this.sortList[categoryIndex];
|
|
|
+ const newIndex = itemIndex + direction;
|
|
|
+
|
|
|
+ // 确保不超出当前分类的范围
|
|
|
+ if (newIndex < 0 || newIndex >= category.list.length) return;
|
|
|
+
|
|
|
+ const item = category.list.splice(itemIndex, 1)[0];
|
|
|
+ category.list.splice(newIndex, 0, item);
|
|
|
+ this.handleDragEnd(categoryIndex);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 序号变更处理(仅在当前分类内)
|
|
|
+ handleNumberChange(categoryIndex, value, itemIndex) {
|
|
|
+ const category = this.sortList[categoryIndex];
|
|
|
+ const targetIndex = value - 1;
|
|
|
+
|
|
|
+ // 验证目标位置是否在当前分类范围内
|
|
|
+ if (targetIndex < 0 || targetIndex >= category.list.length) return;
|
|
|
+ if (targetIndex === itemIndex) return;
|
|
|
+
|
|
|
+ const item = category.list.splice(itemIndex, 1)[0];
|
|
|
+ category.list.splice(targetIndex, 0, item);
|
|
|
+ this.handleDragEnd(categoryIndex);
|
|
|
+ },
|
|
|
+
|
|
|
+ handleClose() {
|
|
|
+ this.visible = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ handleConfirm() {
|
|
|
+ this.$emit('confirm', this.sortList);
|
|
|
+
|
|
|
+
|
|
|
+ this.visible = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.sort-container {
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ .tip-box {
|
|
|
+ background: #EFF6FF;
|
|
|
+ padding: 10px;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ border-radius: 4px;
|
|
|
+ color: #3271FF;
|
|
|
+ font-size: 13px;
|
|
|
+
|
|
|
+ .el-icon-info {
|
|
|
+ margin-right: 5px;
|
|
|
+ color: #3271FF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .sort-list {
|
|
|
+ max-height: 500px;
|
|
|
+ overflow-y: auto;
|
|
|
+
|
|
|
+ .category-section {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .category-header {
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #333;
|
|
|
+ padding-left: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .draggable-container {
|
|
|
+ padding: 5px 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sort-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 10px;
|
|
|
+ border: 1px solid #EBEEF5;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #f5f7fa;
|
|
|
+ }
|
|
|
+
|
|
|
+ .drag-handle {
|
|
|
+ cursor: move;
|
|
|
+ color: #909399;
|
|
|
+ margin-right: 10px;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .move-btns {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|