Bootstrap

实现点击表格中的邀请码并复制到剪贴板的功能


要实现点击表格中的邀请码并复制到剪贴板的功能,可以使用 JavaScript 的 Clipboard API。以下是如何在你的 invite-code-list.vue 文件中进行相应的修改:

实现步骤

  1. 添加点击事件处理函数:在 methods 中添加一个处理函数,用来处理点击并复制邀请码的操作。
  2. 在表格列中添加点击事件:在显示邀请码的表格列中添加 @click 事件,调用复制函数。

修改代码

1. 添加复制邀请码的处理函数

methods 中添加一个处理函数 copyInviteCode,用于处理点击并复制邀请码的操作。

private copyInviteCode(inviteCode: string) {
  const input = document.createElement('input');
  input.value = inviteCode;
  document.body.appendChild(input);
  input.select();
  document.execCommand('copy');
  document.body.removeChild(input);
  this.$message.success('邀请码已复制到剪贴板');
}
2. 在表格列中添加点击事件

在显示邀请码的表格列中添加 @click 事件,调用 copyInviteCode 函数。

<el-table-column
  prop="inviteCode"
  label="邀请码"
  header-align="center"
  align="center"
>
  <template slot-scope="scope">
    <div class="code-desc">
      <span @click="copyInviteCode(scope.row.inviteCode)" class="copyable-code">{{ scope.row.inviteCode }}</span>
    </div>
  </template>
</el-table-column>
3. 添加样式

为了更好地用户体验,建议添加一些样式,使得邀请码在悬停时显示为可点击状态。

<style lang="scss" scoped>
.invite-code-wrap {
  .operate-bar {
    margin-bottom: 16px;
  }
  :deep(.el-table) {
    th {
      border-right: none;
      cursor: default; // 设置光标为默认状态
      user-select: none; // 禁止文本选择
      .el-resizer {
        display: none !important;
      }
    }

    td {
      border-right: none;
    }
  }

  .copyable-code {
    cursor: pointer;
    color: #409EFF;
    &:hover {
      color: #66b1ff;
      text-decoration: underline;
    }
  }

  .pagination-container {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;
  }
}
</style>
完整的 invite-code-list.vue 文件
<template>
  <el-dialog
    :visible="visible"
    :before-close="handleClose"
    :close-on-click-modal="false"
    title="邀请码"
    width="1600px"
    append-to-body
    :modal-append-to-body="false"
    destroy-on-close
    @open="console.log('el-dialog 打开了')"
  >
    <div class="invite-code-wrap">
      <div class="operate-bar">
        <el-button
          type="primary"
          size="mini"
          @click="handleAdd"
        >
          添加
        </el-button>
      </div>

      <div class="table">
        <el-table
          :data="tableData"
          :height="tableHeight"
          border
          :fit="true"
          :resize-observer="false"
          style="width: 100%;"
        >
          <el-table-column
            prop="id"
            label="邀请码ID"
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="inviteCode"
            label="邀请码"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              <div class="code-desc">
                <span @click="copyInviteCode(scope.row.inviteCode)" class="copyable-code">{{ scope.row.inviteCode }}</span>
              </div>
            </template>
          </el-table-column>
          <el-table-column
            prop="invitorId"
            label="邀请人"
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="inviteLevel"
            label="邀请层级"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              {{ scope.row.inviteLevel === 0 ? '不限' : `${scope.row.inviteLevel}级` }}
            </template>
          </el-table-column>
          <el-table-column
            prop="createdDate"
            label="生成时间"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              <div class="time-wrap">
                {{ formatDate(scope.row.createdDate).date }}<br />
                {{ formatDate(scope.row.createdDate).time }}
              </div>
            </template>
          </el-table-column>
          <el-table-column
            prop="expireTime"
            label="失效时间"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              <div class="time-wrap">
                {{ formatDate(scope.row.expireTime).date }}<br />
                {{ formatDate(scope.row.expireTime).time }}
              </div>
            </template>
          </el-table-column>
          <el-table-column
            prop="remark"
            label="备注"
            show-overflow-tooltip
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="status"
            label="状态"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              <el-tag
                :type="getStatusType(scope.row.status)"
                size="mini"
              >
                {{ getStatusText(scope.row.status) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column
            prop="boundPhone"
            label="绑定手机号"
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="weixinNickname"
            label="微信昵称"
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="weixinHeadimg"
            label="微信头像"
            header-align="center"
            align="center"
          />
          <el-table-column
            prop="boundWxUid"
            label="绑定微信UID"
            show-overflow-tooltip
            header-align="center"
            align="center"
            width="100"
          />
          <el-table-column
            label="操作"
            header-align="center"
            align="center"
          >
            <template slot-scope="scope">
              <div class="operation-cell">
                <el-button
                  type="text"
                  size="mini"
                  class="operation-btn"
                  @click="handleLock(scope.row)"
                >
                  {{ scope.row.isLocked === 0 ? '锁定' : '解锁' }}
                </el-button>
                <el-button
                  type="text"
                  size="mini"
                  class="operation-btn"
                  @click="handleRemark(scope.row)"
                >
                  备注
                </el-button>
                <el-button
                  type="text"
                  size="mini"
                  class="operation-btn"
                  @click="handleLog(scope.row)"
                >
                  日志
                </el-button>
              </div>
            </template>
          </el-table-column>
          <el-table-column
            v-if="false"
            prop="admin"
            label="管理人"
            header-align="center"
            align="center"
          />
        </el-table>
      </div>

      <div class="pagination-container">
        <el-pagination
          :current-page="currentPage"
          :page-size="pageSize"
          :total="total"
          background
          layout="prev, pager, next"
          @current-change="handleCurrentChange"
        />
      </div>
    </div>
    <invite-code-add
      :visible.sync="addVisible"
      @success="handleAddSuccess"
    />
  </el-dialog>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import { ElMessageBox } from 'element-ui/types/message-box'
import InviteCodeAdd from './invite-code-add.vue'
import { AppModule } from '@/store/modules/app'
import { getInviteCodeList } from '@/api/invite-code'
import { getInviteCodeListByInvitor } from '@/api/invite-code'
import { UserModule } from '@/store/modules/user'

type MessageBoxData = {
  value: string
  action: 'confirm' | 'cancel'
}

@Component({
  name: 'InviteCodeForm',
  components: {
    InviteCodeAdd
  }
})
export default class extends Vue {
  get tableHeight() {
    return Math.floor(AppModule.tableHeight * 2 / 3)
  }

  @Prop({ default: false })
  private visible!: boolean

  private tableData = []
  private currentPage = 1
  private pageSize = 10
  private total = 0

  private addVisible = false

  private getStatusType(status: string) {
    const map: { [key: string]: string } = {
      effective: 'primary',
      expired: 'info',
      locked: 'danger',
      bound: 'success'
    }
    return map[status] || 'info'
  }

  private getStatusText(status: string) {
    const map: { [key: string]: string } = {
      effective: '待绑定',
      expired: '已失效',
      locked: '已锁定',
      bound: '已绑定'
    }
    return map[status] || status
  }

  private formatDate(dateString: string): { date: string; time: string } {
    if (!dateString) {
      return { date: '', time: '' }
    }
    const date = new Date(dateString)
    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    const hours = String(date.getHours()).padStart(2, '0')
    const minutes = String(date.getMinutes()).padStart(2, '0')
    const seconds = String(date.getSeconds()).padStart(2, '0')
    return {
      date: `${year}-${month}-${day}`,
      time: `${hours}:${minutes}:${seconds}`
    }
  }

  private handleClose() {
    this.$emit('update:visible', false)
  }

  private handleAdd() {
    this.addVisible = true
  }

  private async handleAddSuccess() {
    // TODO: 刷新列表数据
    this.addVisible = false
    await this.fetchInviteCodeList()
  }

  private handleLock(row: any) {
    // TODO: 处理锁/解锁逻辑
    this.$confirm(`确定要${row.isLocked === 0 ? '锁定' : '解锁'}该邀请码吗?`, '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
      .then(async() => {
        // TODO: 调用锁定/解锁API
        row.isLocked = row.isLocked === 0 ? 1 : 0
        this.$message.success(`${row.isLocked === 0 ? '解锁' : '锁定'}成功`)
      })
      .catch(() => { })
  }

  private handleRemark(row: any) {
    this.$prompt('请输入备注', '备注', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      inputValue: row.remark,
      inputValidator: (value: string) => {
        return value.length <= 60
      },
      inputErrorMessage: '备注不能超过60个字符'
    })
      .then(async(data: MessageBoxData) => {
        // TODO: 调用保存备注API
        row.remark = data.value
        this.$message.success('备注保存成功')
      })
      .catch(() => { })
  }

  private handleLog(row: any) {
    // TODO: 显示日志记录
    // 可以打开一个新的对话框显示操作日志列表
  }

  private handleCurrentChange(page: number) {
    this.currentPage = page
    // TODO: Add your fetch data logic here
  }

  async mounted() {
    await this.fetchInviteCodeList()
  }

  private async fetchInviteCodeList() {
    try {
      const response = await getInviteCodeListByInvitor({
        invitorId: UserModule.id,
        page: this.currentPage,
        pageSize: this.pageSize
      })
      console.log(response)
      if (response && response.data && response.data.list) {
        this.tableData = response.data.list
        this.total = response.data.total
      } else {
        this.$message.error('获取邀请码列表失败: 返回数据结构不符合预期')
        console.error('getInviteCodeList response data error', response)
      }

    } catch (error) {
      console.error('获取邀请码列表时发生错误', error)
      this.$message.error('获取邀请码列表时发生错误')
    }
  }

  private copyInviteCode(inviteCode: string) {
    const input = document.createElement('input')
    input.value = inviteCode
    document.body.appendChild(input)
    input.select()
    document.execCommand('copy')
    document.body.removeChild(input)
    this.$message.success('邀请码已复制到剪贴板')
  }
}
</script>

<style lang="scss" scoped>
@import '@/styles/_list-page.scss';
.invite-code-wrap {
  .operate-bar {
    margin-bottom: 16px;
  }
  :deep(.el-table) {
    th {
      border-right: none;
      cursor: default; // 设置光标为默认状态
      user-select: none; // 禁止文本选择
      .el-resizer {
        display: none !important;
      }
    }

    td {
      border-right: none;
    }
  }

  .copyable-code {
    cursor: pointer;
    color: #409EFF;
    &:hover {
      color: #66b1ff;
      text-decoration: underline;
    }
  }

  .pagination-container {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;
  }
}
</style>

通过这些修改,你可以实现点击表格中的邀请码并复制到剪贴板的功能,并且提供了用户友好的视觉反馈。

在这里插入图片描述

;