您的位置:首页 > 娱乐 > 八卦 > 个人小程序源码_福州关键词搜索排名_免费域名解析_百度风云排行榜官网

个人小程序源码_福州关键词搜索排名_免费域名解析_百度风云排行榜官网

2024/12/23 8:13:02 来源:https://blog.csdn.net/Yuki_yuhan/article/details/140667420  浏览:    关键词:个人小程序源码_福州关键词搜索排名_免费域名解析_百度风云排行榜官网
个人小程序源码_福州关键词搜索排名_免费域名解析_百度风云排行榜官网

文章目录

  • 01 基础信息
    • 1.1. 技术栈
    • 1.2. 组件设计
      • a. 竖版设计稿
      • b. 横版设计稿
  • 02 技术方案
    • (1)初定义数据
    • (2)注意事项
    • (3)逻辑草图
  • 03 代码示例
    • 3.1. 组件使用
    • 3.2. 组件源码
      • ./TransferPlus/index.vue
      • ./TransferPlus/TransferTable.vue

01 基础信息

1.1. 技术栈

Element-UIVue2lodash

1.2. 组件设计

需求描述:

  1. 【待选择列表】 接收业务的表格数据,支持选择多项并将其添加到【已添加列表】 (勾选或删除操作,两边的列表是同步的);
  2. 【已添加列表】支持本地分页和本地简易搜索功能(已添加的列表数据需要实时同步给业务);

a. 竖版设计稿

穿梭框竖版设计稿

b. 横版设计稿

穿梭框横版设计稿

02 技术方案

(1)初定义数据

// 【待选择列表】外部传输源数据
// 【已添加列表】组件内部控制数据的分页、搜索和展示const props = {sourceList: [], // 源数据columnList: [], // 表格列配置(注:字段类型均为字符串)searchList: [], // 【已添加列表】搜索项(注:与表头对应)refreshTableData: (param)=>{}, // 回调函数total: 0, // 用于控制分页器
}const state = {targetList: [], // 目标数据searchList: [], // 【已添加列表】搜索项
}

(2)注意事项

  1. 【待选择列表】翻页选择时需要记录并回显已选择的行
  2. 【已添加列表】删除后需要继续留在当前页,即要判断删除的是否是最后一页中只有一条的数据
  3. 【待选择列表】更改选择后,【已添加列表】的筛选项或是状态项是否重置?或是维持不变?

(3)逻辑草图

逻辑草图

03 代码示例

3.1. 组件使用

外部可通过 ref 调用的方法:

  1. clearSelection():清空所有选择项;
  2. setPaginationParam({pageNum,pageSize},isFetch):设置源表格分页器参数,若 isFetch 为 true 则会自动调用 fetchSourceListisFetch 默认为 false );
  3. initializeComponent(isFetch):初始化组件,若 isFetch 为 true 则初始化后自动请求源表格数据( isFetch 默认为 false );
  4. this.$refs['transferPlus'].selectList:若要初始化 selectList 可以使用 ref 设置(记得外面包裹 this.$nextTick);

注意事项:

  1. 使用插槽自定义表格列时,是同时应用到两个列表中的;
  2. 组件会通过 selectionChange 事件告知您选择的列表结果;
  3. 特别地,组件一开始不会默认请求源表格数据,所以您需要在使用前自行调用 fetchSourceList 获取 sourceList 等来渲染组件的数据,组件只会在内部的分页状态等有更改的情况下自动调用 fetchSourceList 为您刷新渲染数据;
  4. usePaginationtrue,则组件自动为您控制分页器,但您必须设置好变量(sourceTotal)和源表格数据请求方法(fetchSourceList),并且为了防止您初始请求的分页参数和组件内部定义的默认初始分页参数不同,您可以设置 initSourcePageNuminitSourcePageSize 来同步内外初始化参数;
<template>  <TransferPlus ref="transferPlusRef":sourceList="sourceList" :tableColumnList="tableColumnList" usePagination tableHeight="240" :sourceTotal="sourceTotal" :tableLoading="tableLoading" @fetchSourceList="fetchSourceList" ><!-- "table_"后拼接的是你定义该列的prop --><template #table_tag="{ row, rowIndex }">{{ rowIndex + 1 }}{{row.tag}}</template><!-- 自定义源表格的搜索区域 --><template #source_search><el-input placeholder="请输入课程名称" v-model="queryInfo.title" class="search-input" clearable><el-button slot="append" icon="el-icon-search" @click="searchSourceList"></el-button></el-input></template></TransferPlus>
</template><script>import TransferPlus from '@/components/TransferPlus'export default {components: { TransferPlus },data() {sourceList: [],    tableColumnList: [{ label: '课程id', prop: 'id' },{ label: '课程名称', prop: 'title' },{ label: '课程类型', prop: 'tag' },],tableLoading: false,sourceTotal: 0,queryInfo: {pageNum: 1,pageSize: 10,title: '',tag: '',},}method:{async fetchSourceList (params={pageNum,pageSize}) {this.tableLoading = trueconst { pageNum, pageSize } = this.queryInfothis.queryInfo = {...this.queryInfo,pageNum: params?.pageNum || pageNum,pageSize: params?.pageSize || pageSize,}const res = await getList(this.queryInfo)this.sourceList = res.data.list || []this.sourceTotal = res.data.total || 0this.tableLoading = false},searchSourceList() {// 每次查询时只需要重置穿梭框的页码到 1,并配置自动调用搜索函数this.$refs['transferPlusRef'].setPaginationParam({ pageNum: 1 }, true)},}}</script><style scoped>.search-input {margin-bottom: 12px;width: 100%;height: 32px;}</style>

实现效果图:
实现效果图

3.2. 组件源码

./TransferPlus/index.vue

<!--  
组件使用方式如下:<TransferPlus :sourceList="sourceList" :tableColumnList="tableColumnList" usePagination tableHeight="240" :sourceTotal="sourceTotal" :tableLoading="tableLoading" @fetchSourceList="fetchSourceList" ><template #table_你定义该列的prop="{ columnProps }">{{ columnProps.$index + 1 }}{{columnProps.row.xxx}}</template></TransferPlus>method:{async fetchSourceList (params={pageNum,pageSize}) {this.tableLoading = trueconst res = await getList({ ...this.queryInfo, ...params })this.sourceList = res.data.listthis.sourceTotal = res.data.totalthis.tableLoading = false}}
外部可通过 ref 调用的方法:
1. clearSelection():清空所有选择项;
2. setPaginationParam({pageNum,pageSize},isFetch):设置源表格分页器参数,若 isFetch 为 true 则会自动调用 fetchSourceList( isFetch 默认为 false );
3. initializeComponent(isFetch):初始化组件,若 isFetch 为 true 则初始化后自动请求源表格数据( isFetch 默认为 false );
4. this.$refs['transferPlusRef'].selectList:若要初始化 selectList 可以使用 ref 设置(记得外面包裹 this.$nextTick);
注意事项:
1. 使用插槽自定义表格列时,是同时应用到两个列表中的;
2. 组件会通过 selectionChange 事件告知您选择的列表结果;
3. 特别地,组件一开始不会默认请求源表格数据,所以您需要在使用前自行调用 fetchSourceList 获取 sourceList 等来渲染组件的数据,组件只会在内部的分页状态等有更改的情况下自动调用 fetchSourceList 为您刷新渲染数据;
4. 若 usePagination 为 true,则组件自动为您控制分页器,但您必须设置好变量(sourceTotal)和源表格数据请求方法(fetchSourceList),并且为了防止您初始请求的分页参数和组件内部定义的默认初始分页参数不同,您可以设置 initSourcePageNum 和 initSourcePageSize 来同步内外初始化参数;-->
<template><div :class="direction === 'horizontal' ? 'transfer-horizontal' : ''"><!-- 【待选择列表】 --><div :class="['list-wrapping', { horizontal: direction === 'horizontal' }]"><div class="wrapping-header"><span>待选择列表</span><span>{{ selectLength }}/{{ sourceTotal || sourceList.length }}</span></div><div class="wrapping-content"><!-- 自定义搜索 --><slot name="source_search" /><TransferTableref="sourceTransferTableRef"v-model="selectList":tableList="sourceList":tableColumnList="tableColumnList":tableHeight="tableHeight":total="sourceTotal":initPageNum="initSourcePageNum":initPageSize="initSourcePageSize":usePagination="usePagination":tableLoading="tableLoading":uniqueKey="uniqueKey":selectable="selectable":pagerCount="pagerCount"@fetchTableList="handleFetchTableList"><!-- 使用穿梭表格的自定义列插槽 --><template v-for="(item, index) in tableColumnList" :slot="`inner_table_${item.prop}`" slot-scope="slotData"><span :key="index"><!-- 设置新的插槽提供给消费端自定义列 --><slot :name="`table_${item.prop}`" :columnProps="slotData.columnProps" :row="slotData.columnProps.row" :rowIndex="slotData.columnProps.$index">{{ slotData.columnProps.row[item.prop] || '-' }}</slot></span></template></TransferTable></div></div><!-- 【已添加列表】 --><div :class="['list-wrapping', { horizontal: direction === 'horizontal' }]"><div class="wrapping-header"><span>已添加列表</span><span>{{ selectLength }}</span></div><div class="wrapping-content"><template v-if="selectLength"><el-input placeholder="请输入内容" v-model="searchStr" class="search-input" clearable><el-select slot="prepend" v-model="searchKey" placeholder="请选择" class="search-select" @change="handleSearchKeyChange" value-key="prop"><el-option v-for="item in targetSearchList" :key="item.prop" :label="item.label" :value="item.prop"></el-option></el-select><el-button slot="append" icon="el-icon-search" @click="handleSearchStrChange"></el-button></el-input><TransferTableref="targetTransferTableRef":tableList="targetList":tableColumnList="tableColumnList":tableHeight="tableHeight"tableType="target":uniqueKey="uniqueKey":total="targetTotal":usePagination="usePagination":pagerCount="pagerCount"@removeSelectRow="handleRemoveSelectRow"@fetchTableList="getTargetTableList"><!-- 使用穿梭表格的自定义列插槽 --><template v-for="(item, index) in tableColumnList" :slot="`inner_table_${item.prop}`" slot-scope="slotData"><span :key="index"><!-- 设置新的插槽提供给消费端自定义列 --><slot :name="`table_${item.prop}`" :columnProps="slotData.columnProps" :row="slotData.columnProps.row" :rowIndex="slotData.columnProps.$index">{{ slotData.columnProps.row[item.prop] || '-' }}</slot></span></template></TransferTable></template><div class="empty-box" v-else><el-image class="empty-image" :src="require('@/assets/empty_images/data_empty.png')" /></div></div></div></div>
</template><script>import TransferTable from './TransferTable.vue'import { throttle, differenceBy, filter, isNil, noop } from 'lodash'export default {components: { TransferTable },props: {// 源数据sourceList: {type: Array,default: () => [],},// 表格列配置列表tableColumnList: {type: Array,default: () => [], // {label,prop,align}[]},// 表格数据是否加载中tableLoading: {type: Boolean,default: false,},// 表格高度tableHeight: {type: String | Number,default: 240,},// 【已添加列表】搜索项(注:与表格列配置对应,且仅能搜索字段类型为 String)searchList: {type: Array,default: () => [], // {label,prop}[]},// 源表格总数据的条数sourceTotal: {type: Number,default: 0,},// 源表格初始 pageNum(用于同步消费端初始化请求时的分页参数,进而帮助控制分页器)initSourcePageNum: {type: Number,default: 1,},// 源表格初始 pageSize(用于同步消费端初始化请求时的分页参数,进而帮助控制分页器)initSourcePageSize: {type: Number,default: 10,},// 使用分页器usePagination: {type: Boolean,default: false,},// 唯一标识符(便于定位到某条数据进行添加和移除操作)uniqueKey: {type: String,default: 'id',},// 穿梭框展示方式direction: {type: String,default: 'vertical', // horizontal 左右布局, vertical 上下布局},selectable: {type: Function,default: noop(),},// 页码按钮的数量,当总页数超过该值时会折叠(element规定:大于等于 5 且小于等于 21 的奇数)pagerCount: {type: Number,default: 7,},},data() {return {selectList: [], // 已选择的列表targetList: [], // 已添加列表的回显数据searchKey: '',searchStr: '',targetPageNum: 1,targetPageSize: 10,targetTotal: 10,}},computed: {targetSearchList() {return this.searchList.length ? this.searchList : this.tableColumnList},selectLength() {return this.selectList?.length || 0},},watch: {selectList(newVal) {this.getTargetTableList()this.$emit('selectionChange', newVal)},},mounted() {this.searchKey = this.targetSearchList[0].propthis.targetPageNum = 1this.targetPageSize = 10},methods: {handleFetchTableList(params) {this.$emit('fetchSourceList', params)},handleRemoveSelectRow(rowItem) {this.selectList = differenceBy(this.selectList, [rowItem], this.uniqueKey)},handleSearchStrChange() {// 每次查询时只需要重置穿梭框的页码到 1,并配置自动调用搜索函数this.$refs['targetTransferTableRef'].setPaginationParam({ pageNum: 1 }, true)},handleSearchKeyChange() {// 更新搜索 Key 之后,需要清空搜索字符串this.searchStr = ''this.$refs['targetTransferTableRef'].setPaginationParam({ pageNum: 1 }, true)},getTargetTableList(params = null) {const targetTableList = filter(this.selectList, (item) => {if (this.searchStr) {const itemValueToString = isNil(item[this.searchKey]) ? '' : JSON.stringify(item[this.searchKey])return itemValueToString.includes(this.searchStr)} else {return true}})this.targetTotal = targetTableList.lengthif (params) {this.targetPageNum = params.pageNumthis.targetPageSize = params.pageSize}// 前端分页const startIndex = (this.targetPageNum - 1) * this.targetPageSizeconst endIndex = this.targetPageNum * this.targetPageSizethis.targetList = targetTableList.slice(startIndex, endIndex)},clearSelection() {// 清空所有选择项(用于消费端设置的 ref 调用)this.selectList = []this.targetPageNum = 1this.targetPageSize = 10this.searchKey = this.targetSearchList[0].prop},setPaginationParam({ pageNum, pageSize }, isFetch) {// 设置源表格分页器参数(用于消费端设置的 ref 调用)//  若 isFetch 为 true,则自动调用消费端传进来的回调搜索方法this.$refs['sourceTransferTableRef'].setPaginationParam({ pageNum, pageSize }, isFetch)},initializeComponent(isFetch) {// 初始化组件(用于消费端设置的 ref 调用)//  若 isFetch 为 true,则自动调用消费端传进来的回调搜索方法this.clearSelection()this.setPaginationParam({ pageNum: this.initSourcePageNum || 1, pageSize: this.initSourcePageSize || 10 }, isFetch)},},}
</script><style lang="scss" scoped>.transfer-horizontal {display: flex;}.list-wrapping {margin-bottom: 12px;border-radius: 2px;border: 1px solid #d9d9d9;background: #fff;overflow: hidden;}.horizontal {flex: 1;margin-right: 20px;margin-bottom: 0px;&:last-child {margin: 0px;}}.wrapping-header {width: 100%;padding: 10px 20px;height: 40px;display: flex;justify-content: space-between;align-items: center;border-bottom: 1px solid #d9d9d9;background: #f5f5f5;color: #333;font-size: 14px;line-height: 20px;}.wrapping-content {padding: 12px;width: 100%;}.search-input {margin-bottom: 12px;max-width: 500px;height: 32px;}.search-select {width: 120px;}.empty-box {display: flex;align-items: center;justify-content: center;width: 100%;height: 100%;margin: 40px 0px;}.empty-image {width: 150px;height: 150px;}:deep(.search-input .el-input-group__prepend) {background-color: #fff;}:deep(.el-select .el-input .el-select__caret) {color: #3564ff;}
</style>

./TransferPlus/TransferTable.vue

<template><div><!-- 表格区域 --><el-tableref="transferTable"v-loading="tableLoading":border="true":data="tableList"size="mini":stripe="true":height="tableHeight || 'auto'":row-class-name="getTableRowClassName":header-cell-style="{background: '#F1F1F1',}"@select="handleSelect"@select-all="handleSelectAll"><el-table-column type="index" align="center"></el-table-column><el-table-column type="selection" width="50" v-if="tableType === 'source'" :selectable="selectable"></el-table-column><el-table-column v-for="(item, index) in tableColumnList" :key="item.prop || index" :label="item.label" :prop="item.prop" :align="item.align || 'left'" :width="item.width || 'auto'" show-overflow-tooltip><template #default="columnProps"><slot :name="`inner_table_${item.prop}`" :columnProps="columnProps"><span>{{ columnProps.row[item.prop] }}</span></slot></template></el-table-column><el-table-column fixed="right" label="操作" width="70" align="center" v-if="tableType === 'target'"><template slot-scope="scope"><el-button @click="handleRemoveRowItem(scope.row, scope.$index)" type="text" icon="el-icon-delete" size="medium"></el-button></template></el-table-column></el-table><!-- 分页器区域 --><div v-if="usePagination" class="pagination-box"><!-- 实现两侧分布的分页器布局:使用两个分页器组件 + 不同 layout 组成 --><el-pagination background :current-page="pageNum" :layout="layoutLeft" :page-size="pageSize" :pager-count="pagerCount" :total="total" @current-change="handleCurrentChange" @size-change="handleSizeChange" /><el-pagination background :current-page="pageNum" :layout="layoutRight" :page-size="pageSize" :pager-count="pagerCount" :total="total" @current-change="handleCurrentChange" @size-change="handleSizeChange" /></div></div>
</template><script>import { differenceBy, uniqBy, noop } from 'lodash'export default {props: {// 已勾选的数组value: {type: Array,default: () => [],require: true,},// 表格数据tableList: {type: Array,default: () => [],},// 表格列配置列表tableColumnList: {type: Array,default: () => [], // {label,prop,align}[]},// 表格数据是否加载中tableLoading: {type: Boolean,default: false,},// 表格高度tableHeight: {type: String | Number,default: 240,},// 表格数据类型tableType: {type: String,default: 'source', // source 源列表,target 目标列表},// 【已添加列表】搜索项(注:与表格列配置对应,且仅能字段类型为 String)searchList: {type: Array,default: () => [], // {label,prop,align}[]},// 分页后表格总数据的条数total: {type: Number,default: 0,},// 初始 pageNuminitPageNum: {type: Number,default: 1,},// 初始 pageSizeinitPageSize: {type: Number,default: 10,},// 使用分页器usePagination: {type: Boolean,default: false,},// 唯一标识符(便于定位到某条数据进行添加和移除操作)uniqueKey: {type: String,default: 'id',},// Function 的返回值用来决定这一行的 CheckBox 是否可以勾选selectable: {type: Function,default: noop(),},// 页码按钮的数量,当总页数超过该值时会折叠(element规定:大于等于 5 且小于等于 21 的奇数)pagerCount: {type: Number,default: 7,},},data() {return {layoutLeft: 'total',layoutRight: 'sizes, prev, pager, next',pageNum: 1,pageSize: 10,preSelectList: [], // 上一次选择的数据(点击分页器就清空)stashSelectList: [], // 暂存数据,便于点击页码后,还能保存前一页的数据isNeedToggle: true, // 是否需要勾选该页已选择项(用于换页后的回显选择项)isTableChangeData: false, // 是否是当前表格造成选择项的变化(用于同步【待选择列表】的勾选项)}},computed: {currentPageSelectList() {const currentSelectList = []this.stashSelectList?.forEach((item) => {const currentRow = this.tableList?.find((row) => row[this.uniqueKey] === item[this.uniqueKey])if (currentRow) {currentSelectList.push(currentRow)}})return currentSelectList},},watch: {value(newVal) {this.stashSelectList = newVal || []// 只有在其他地方修改了选择表格数据后,才刷新覆盖勾选项(当前表格修改选择项是双向绑定的,所以不需要刷新覆盖勾选项),实现精准回显和两表格的联动if (!this.isTableChangeData) {this.handleToggleSelection()}// 当暂存的选择列表为空时,需要同步更新 preSelect 为空数组,以便下次选择时进行判断是增加选择项还是减少选择项if (!this.stashSelectList.length) {this.preSelectList = []}this.isTableChangeData = false},tableList() {if (this.isNeedToggle) {this.preSelectList = this.currentPageSelectListthis.handleToggleSelection()this.isNeedToggle = false}},},mounted() {this.pageNum = this.initPageNum || 1this.pageSize = this.initPageSize || 110// 解决右侧固定操作栏错位问题this.$nextTick(() => {this.$refs.transferTable.doLayout()})this.$emit('selectionChange', [])},methods: {getTableRowClassName({ rowIndex }) {if (rowIndex % 2 == 0) {return ''} else {return 'stripe-row'}},fetchTableList(pageNum = 1) {if (this.usePagination) {// 若不是页码更改触发,则默认将 pageNum 重置为 1this.pageNum = pageNumconst params = {pageNum: this.pageNum,pageSize: this.pageSize,}this.$emit('fetchTableList', params)} else {this.$emit('fetchTableList')}},setPaginationParam({ pageNum, pageSize }, isFetch = false) {// 设置分页器参数(用于消费端设置的 ref 调用)this.pageNum = pageNum || this.pageNumthis.pageSize = pageSize || this.pageSizethis.isNeedToggle = trueif (isFetch) {this.fetchTableList()}},handleSizeChange(val) {this.pageSize = valthis.isNeedToggle = truethis.fetchTableList()},handleCurrentChange(val) {this.isNeedToggle = truethis.fetchTableList(val)},handleStashSelectList(isAdd = true, list = []) {if (isAdd) {// 暂存数组中增加,并兜底去重this.stashSelectList = uniqBy([...this.stashSelectList, ...list], this.uniqueKey)} else {// 暂存数组中移除this.stashSelectList = differenceBy(this.stashSelectList, list, this.uniqueKey)}this.isTableChangeData = truethis.$emit('input', this.stashSelectList)this.$emit('selectionChange', this.stashSelectList)},handleSelect(selectList, row) {// 判断是否是增加选择项const isAddRow = this.preSelectList.length < selectList.lengththis.handleStashSelectList(isAddRow, [row])// 更新当前页记录的上次数据this.preSelectList = [...selectList]},handleSelectAll(selectList) {// 判断是否是全选(需要考虑两个数组长度相等的情况)const isAddAll = this.preSelectList.length <= selectList.length// 更新当前页记录的上次数据this.handleStashSelectList(isAddAll, isAddAll ? selectList : this.preSelectList)this.preSelectList = [...selectList]},handleRemoveRowItem(rowItem, rowIndex) {const remainderPage = this.total % this.pageSize ? 1 : 0const pageNumTotal = parseInt(this.total / this.pageSize) + remainderPageconst isLastPageOnlyOne = rowIndex === 0 && this.pageNum === pageNumTotal// 判断删除的是否是最后一页中只有一条的数据if (isLastPageOnlyOne && this.pageNum > 1) {// 若是,则 pageNum 需要往前调整一页,因为删除后最后一页不存在this.handleCurrentChange(this.pageNum - 1)}this.$emit('removeSelectRow', rowItem)},handleToggleSelection() {this.$nextTick(() => {// 先清除所有勾选状态this.$refs.transferTable.clearSelection()if (this.currentPageSelectList.length) {// 再依次勾选当前页存在的行this.currentPageSelectList.forEach((item) => {this.$refs.transferTable.toggleRowSelection(item, true)})}})},},}
</script><style scoped>/* 表格斑马自定义颜色 */:deep(.el-table__row.stripe-row) {background: #f9f9f9;}/* 表格操作栏按钮取消间距 */:deep(.el-button) {padding: 0px;}/* 表格操作栏按钮固定大小 */:deep(.el-icon-delete::before) {font-size: 14px !important;}.pagination-box {display: flex;justify-content: space-between;}
</style>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com