其他分享
首页 > 其他分享> > uniapp + uView使用AntV F6 + table表格插件使用

uniapp + uView使用AntV F6 + table表格插件使用

作者:互联网

首先看页面效果:

AntV官网下载F6文件到项目中

<template>

<view class="page">

<!-- 导航栏 -->

<b-nav-bar class="title">

<template slot="left">

<view @click="nativeBack" class="iconfont icon-zuofanhui nBack ml15"></view>

</template>

<view>{{navTitle}}</view>

</b-nav-bar>

<!-- 树状图 -->

<view class="page over_hidden">

<!-- #ifdef APP-PLUS || H5 -->

<view :id="option.id" :option="option" :treeDom="treeDom" :change:treeDom="treeGraph.changeTreeGraphSize"

:change:option="treeGraph.changeTreeGraphData"

style="width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;"></view>

<!-- #endif -->

<!-- #ifndef APP-PLUS || H5 -->

<view>非 APP、H5 环境不支持</view>

<!-- #endif -->

</view>

<!-- 表格弹窗 -->

<u-popup :show="showLinkTable" mode="bottom" :round="10" class="tablePopup">

<view class="tableHeader">

<scroll-view class="tableTitle" scroll-x="true">

<view style="display: inline-block;">{{tableTitle}}</view>

</scroll-view>

<view class="tableIcon" @click="closePopup">

<text class="iconfont icon-shanchu1"></text>

</view>

</view>

<view class="tableContent">

<view class="time">

最新时间:{{newTime}}

</view>

<lyy-table :headerFixed="true" :columnFixed="1" :emptyString="''" :contents="contentsData"

:headers="columnsData" :sortWays="handleSortByColumn(contentsData)"

@click="oneRowClick(contentsData)" class="lyyTable" @row-click="rowclick"></lyy-table>

</view>

</u-popup>

<!-- 点击问号注释说明弹窗 -->

<u-popup :show="showExplain" mode="bottom" :round="10" class="explainPopup">

<view class="explainHeader">

<scroll-view class="explainTitle" scroll-x="true">

<view style="display: inline-block;">{{explainTitle}}</view>

</scroll-view>

<view class="explainIcon" @click="canclePopup">

<text class="iconfont icon-shanchu1"></text>

</view>

</view>

<view class="explainContent">

{{nodeInterpret}}

</view>

</u-popup>

</view>

</template>

 

<script>

import { // 引入vuex中存储的iOS刘海屏高度

mapState

} from 'vuex';

export default {

data() {

return {

navTitle: '', // 标题

option: { // 导图数据

id: "canvasId",

data: '',

},

treeDom: { // 导图宽高

width: null,

height: null

}

}

},

computed: {

...mapState(['stabarHeight']) //刘海屏高度存储在vuex里面

},

mounted() {

uni

.createSelectorQuery()

.select("#canvasId")

.boundingClientRect((data) => {

this.treeDom = data;

})

.exec();

},

methods: {

nativeBack() {

uni.navigateTo({

url: `/pages/look-company/index/index`

})

},

}

}

</script>

</script>

 

<script module="treeGraph" lang="renderjs">

import F6 from '@/static/common/js/F6/f6.min.js' // 引入F6文件

import TreeGraph from '@/static/common/js/F6/extends/graph/treeGraph.js' // 引入F6文件

import { // 接口方法

queryFirstnodeList,

fetchGraphChildren,

queryCompanyTable

} from '@/api/company.js'

import { // js文件中公共方法引入

computeWidthHeight,

addCollapseNode,

handleChildrenData,

getArcPosition

} from './graphConfig';

import { // 自己写的获取session方法

getSession

} from '@/util/storage';

F6.registerGraph('TreeGraph', TreeGraph)

// 折叠图标

const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) {

return [

['M', x - r, y],

['L', x + r, y],

]

}

// 展开图标

const EXPAND_ICON = function EXPAND_ICON(x, y, r) {

return [

['M', x - r, y],

['L', x + r, y],

['M', x, y - r],

['L', x, y + r]

]

}

 

// 默认边和箭头样式

const defaultEdgeStyle = {

stroke: '#3849B4',

endArrow: {

path: 'M 0,0 L 5,4 L 5,-4 Z',

lineWidth: 1,

fill: '#3849B4',

},

}

export default {

data() {

return {

graph: null,

treeData: {},

graphReady: false,

companyName: '', // 公司名称

companyCode: '', // 公司代码

topTreeData: [], // 二级节点

showLinkTable: false, // 是否显示表格弹窗

tableId: '', // 表格id

tableName: '', // 表名

contentsData: [], //表格数据

columnsData: [], //表头数据

tableTitle: '', // 表格标题

newTime: '', //最新时间

showExplain: false, // 是否显示点击问号注释说明弹窗

explainTitle: '', // 注释说明弹窗标题

nodeInterpret: '', // 注释说明弹窗内容

}

},

created() {

this.initData() // 初始化一、二级节点数据

},

mounted() {

this.init() // 初始化F6

},

onLoad(option) {

this.navTitle = `${option.title}(${option.code})` // 页面标题

this.companyName = option.title // 公司名

this.companyCode = option.code // 公司代码

},

methods: {

// 初始化一、二级节点

async initData() {

await this.requestTopTreeNode()

this.setDefaultNode()

},

// 获取二级分类节点

async requestTopTreeNode() {

try {

let res = await queryFirstnodeList({

token: sessionStorage.getItem('token')

})

res = res.data || []

res.forEach((item) => {

item.level = 2

item.hasChildren = true // 隐藏展示/折叠图表

item.collapsed = true // 当前折叠状态

item.isGetChildren = false // 是否已加载了children

})

this.topTreeData = res

} catch (err) {

err.msg && this.$alert(err.msg, '提示')

}

},

// 设置初始树数据

setDefaultNode(data) {

if (!data) {

const children = JSON.parse(JSON.stringify(this.topTreeData))

data = {

id: 'root',

name: this.companyName,

children,

level: 1

}

this.treeData = data

}

if (this.graphReady) {

this.showLinkTable = false

this.graph.data(this.treeData)

this.graph.render()

this.graph.fitCenter()

} else {

const unwatch = this.$watch('graphReady', (val) => {

if (val) {

this.setDefaultNode(data)

unwatch()

}

})

}

},

// 初始化

init() {

this.treeDom.width = wx.getSystemInfoSync().screenWidth // 获取屏幕宽度,让图水平居中

let sysInfo = getSession('systemInfo')

if (sysInfo.platform.indexOf('ios') > -1) {

this.treeDom.height = wx.getSystemInfoSync().screenHeight - 44 - this.stabarHeight // 获取ios屏幕高度,让图垂直居中

} else {

this.treeDom.height = wx.getSystemInfoSync().screenHeight - 44 // 获取安卓屏幕高度,让图垂直居中

}

// 绘制节点

F6.registerNode('icon-node', {

draw(cfg, group) {

const styles = this.getShapeStyle(cfg);

const {

labelCfg = {}

} = cfg;

// const w = styles.width;

// const h = styles.height;

let keyShape = {}

// 根节点,高度固定,宽度根据公司名调整

if (cfg.level === 1) {

const w = 75

const h = 30

keyShape = group.addShape('rect', {

attrs: {

width: w,

height: h,

x: -w / 2,

y: -h / 2,

fill: '#3849B4',

radius: 12,

},

})

group.addShape('text', {

attrs: {

...labelCfg.style,

text: cfg.name,

x: 0,

y: 0,

fontSize: 11,

fill: '#ffffff',

},

})

} else {

// 不管二/三级几点是否有展开/折叠图标宽度都不计算上,在layout中会考虑

let w = 75

let h = 30

let content = ''

// 三级以上节点宽高动态计算

if (cfg.level >= 3) {

const res = computeWidthHeight({

content: cfg.content

})

w = res.width - 30

h = res.height

content = res.content

}

cfg.hasChildren && addCollapseNode(group, w, cfg)

// 配置了有展开/折叠图标则渲染

// 二级分类节点,宽高固定

if (cfg.level === 2) {

keyShape = group.addShape('rect', {

attrs: {

fill: '#EEF2F8',

stroke: '#3849B4',

radius: 6,

width: w,

height: h,

fontSize: 11,

x: -w / 2,

y: -h / 2

},

})

group.addShape('text', {

attrs: {

...labelCfg.style,

text: cfg.name,

x: 0,

y: 0,

fontSize: 11

},

name: 'collapse-icon'

})

} else {

// 三级以上节点配置

keyShape = group.addShape('rect', {

attrs: {

width: w,

height: h,

x: -w / 2,

y: -h / 2,

fill: '#E5E5E5',

radius: 6,

fontSize: 11

},

})

// 上部关系节点名称

group.addShape('rect', {

attrs: {

x: 1 - w / 2,

y: 1 - h / 2,

width: w - 2,

height: 32,

fill: '#EEF2F8',

radius: [6, 6, 0, 0],

fontSize: 11

},

})

group.addShape('text', {

attrs: {

...labelCfg.style,

text: cfg.name,

x: 1,

y: 18 - h / 2,

},

name: 'collapse-icon'

})

// 判断是否有说明文字(即?)

if (cfg.nodeInterpret) {

group.addShape('text', {

attrs: {

...labelCfg.style,

x: w / 2 - 12,

y: 19 - h / 2,

fontFamily: 'iconfont', // 对应css里面的font-family: "iconfont";

textAlign: 'right',

fill: '#999999',

text: '\ue715',

cursor: 'pointer',

lineHeight: 12,

fontSize: 11

},

name: 'interpret-icon',

})

}

// 下方实体节点内容

group.addShape('rect', {

attrs: {

x: 1 - w / 2,

y: 34 - h / 2,

width: w - 2,

height: h - 35,

fill: '#ffffff',

fontSize: 11,

radius: [0, 0, 6, 6]

},

})

// 可能有多行文字

for (let i = 0, l = content.length; i < l; i++) {

const attrs = {

...labelCfg.style,

text: content[i],

x: 0,

y: 34 - h / 2 + 14 + 14 * i,

textAlign: 'center',

fill: '#999999',

fontSize: 11,

lineHeight: 14

}

// 如果只有一行文字,设置y居中

if (l === 1) {

attrs.y = 17

}

const config = {

attrs

}

// 可点击节点样式

if (cfg.showType === '3') {

config.attrs.fill = '#3849B4'

config.attrs.cursor = 'pointer'

config.name = 'link-text'

} else {

config.name = 'collapse-icon'

}

group.addShape('text', config)

}

}

}

return keyShape;

},

update(cfg, item) {

const group = item.getContainer();

const icon = group.find((e) => e.get('name') === 'collapse-icon');

// 如果不展示展开/折叠,调整样式造成视觉隐藏

if (!cfg.hasChildren) {

icon.attr('lineWidth', 0)

icon.attr('cursor', 'auto')

const iconBox = group.find((e) => e.get('name') === 'marker-box')

iconBox.attr('width', 0)

iconBox.attr('stroke', '#ffffff')

} else {

icon.attr('symbol', cfg.collapsed ? EXPAND_ICON : COLLAPSE_ICON);

}

}

}, 'rect');

// 绘制连线

F6.registerEdge('flow-line', {

draw(cfg, group) {

const {

startPoint,

endPoint,

targetNode,

sourceNode,

style: {

stroke,

endArrow

}

} = cfg;

let path = null

// 是否向左

const isLeft = startPoint.x > endPoint.x

// 因为设置的连接点为容器的中心

const sourceNodeWidth = sourceNode._cfg.originStyle.width

startPoint.x = startPoint.x + (isLeft ? -sourceNodeWidth / 2 : sourceNodeWidth / 2)

const targetNodeWidth = targetNode._cfg.originStyle.width

endPoint.x = endPoint.x + (isLeft ? targetNodeWidth / 2 : -targetNodeWidth / 2)

startPoint.x = startPoint.x + (sourceNode._cfg.model.hasChildren ? (isLeft ? -16 : 16) : 0)

// 如果两个节点中心点纵向距离小于10直接绘一条线

if (Math.abs(startPoint.y - endPoint.y) < 8) {

path = [

['M', startPoint.x, startPoint.y],

['L', startPoint.x + (isLeft ? -10 : 10), startPoint.y],

['L', startPoint.x + (isLeft ? -10 : 10), endPoint.y],

['L', endPoint.x, endPoint.y]

]

} else {

// 判断弧线部分向上/下

const isTop = startPoint.y < endPoint.y

// 计算弧线开始/结束坐标

const arcStartX = startPoint.x + (isLeft ? -10 : 10)

const arcStartY = endPoint.y + (isTop ? -4 : 4)

const arcEndX = arcStartX + (isLeft ? -4 : 4)

const arcEndY = endPoint.y

// 取到弧线的控制点坐标(上下2个控制点)

const arcControlList = getArcPosition(arcStartX, arcStartY, arcEndX, arcEndY, 16)

const arcControlItem = isLeft ? (isTop ? arcControlList.bottom : arcControlList.top) :

(isTop ? arcControlList.top : arcControlList.bottom)

// 绘制路径

path = [

['M', startPoint.x, startPoint.y],

['L', arcStartX, startPoint.y],

['L', arcStartX, arcStartY],

['Q', arcControlItem.x, arcControlItem.y, arcEndX, arcEndY],

['L', endPoint.x, arcEndY]

];

}

const shape = group.addShape('path', {

attrs: {

stroke,

path,

endArrow

}

});

return shape;

}

});

// 实例化F6

this.graph = new F6.TreeGraph({

container: this.option.id,

width: this.treeDom.width,

height: this.treeDom.height,

animate: true,

// fitView: true, // 画布自适应

// fitCenter: true, //图将会被平移,图的中心将对齐到画布中心,但不缩放。优先级低于 fitView。

minZoom: 0.2,

maxZoom: 1.5,

linkCenter: true, //指定边是否连入节点的中心

modes: {

default: ['drag-canvas', 'zoom-canvas'],

},

defaultNode: {

type: 'icon-node',

anchorPoints: [

[0, 0.5],

[1, 0.5],

],

labelCfg: {

style: {

fill: '#3849B4',

fontSize: 12,

textAlign: 'center',

textBaseline: 'middle',

},

},

},

defaultEdge: {

type: 'flow-line',

style: defaultEdgeStyle,

},

layout: { // 节点自定义布局

type: 'compactBox',

direction: 'H',

getId(d) {

return d.id;

},

getHeight(d) {

if (d.level >= 3) {

const {

height

} = computeWidthHeight({

content: d.content

})

return height

} else {

return 30

}

},

getWidth(d) {

const {

level

} = d

if (level === 1) {

return 75

} else if (level === 2) {

return 75

} else {

let {

width

} = computeWidthHeight({

content: d.content

})

  if (d.hasChildren) {

width += 16

}

return width

}

},

getVGap() {

return 20;

},

getHGap(d) {

if (d.x > 0) {

return 60

} else {

return 25

}

}

},

})

// 监听点击事件

this.graph.on('node:tap', this.graphNodeClick);

this.graphReady = true

this.graph.data(this.treeData);

this.graph.render();

},

// 元素点击事件

async graphNodeClick(evt) {

const {

item,

target

} = evt;

// const targetType = target.get('type');

const name = target.get('name')

const model = item.getModel()

// 增加元素

if (model.hasChildren && name === 'collapse-icon') {

this.haneldCollapse(item)

} else if (name === 'link-text') {

this.handleLink(item)

//显示加载框

uni.showLoading({

title: '加载中',

mask: true

});

} else if (name === 'interpret-icon') {

this.showExplain = true

this.nodeInterpret = model.nodeInterpret

this.explainTitle = model.name

}

},

// 处理展开折叠

async haneldCollapse(item) {

const {

graph

} = this

const model = item.getModel()

let collapsed = model.collapsed

let hasChildren = model.hasChildren

if (model.collapsed && !model.isGetChildren) {

const id = model.id.split('_')[0];

try {

const res = await fetchGraphChildren(id, this.companyCode);

if (!res.data || res.data.length == 0) {

model.hasChildren = false;

} else {

const children = handleChildrenData(res.data, model.level + 1);

if (children.length) {

model.children = children;

} else {

model.hasChildren = false

}

}

} catch (err) {

model.hasChildren = false

}

model.isGetChildren = true;

}

collapsed = !collapsed;

model.collapsed = collapsed;

graph.updateChild(model, model.id);

const updateParams = {

collapsed

}

if (hasChildren !== model.hasChildren) {

updateParams.hasChildren = model.hasChildren

}

graph.updateItem(item, updateParams);

},

// 获取可点击实体表格信息

async handleLink(item) {

const model = item.getModel()

this.tableId = model.id.split('_')[0];

this.tableName = model.content.join('')

let params = {

id: this.tableId,

name: this.tableName

}

const res = await queryCompanyTable(params)

if (res.code == 0) {

this.tableTitle = res.data.name

this.newTime = res.data.latestTime

// this.contentsData = res.data.resultList

this.showLinkTable = true

res.data.titles.forEach(item => {

this.handleSortByColumn({

data: res.data.resultList,

order: 'asc',

property: item.field

})

let obj = {

label: item.fieldTitle,

key: item.field,

width: (40 + item.fieldTitle.length * 16) + 'px',

sort: true

}

// obj.label == ''

this.columnsData.push(obj)

})

//隐藏加载框

uni.hideLoading();

} else {

//隐藏加载框

uni.hideLoading();

uni.$u.toast(res.msg)

}

},

      // 点击表格第一列跳转当前页面  

rowclick(e) {

uni.redirectTo({

url: `/pages/look-company/companyDetail/companyDetail?title=${e.Stknme}&code=${e.Stkcd}`

})

},

// 排序要求''排在最底下

handleSortByColumn({

data,

order,

property

}) {

let dataList = data

if (order) {

// 是否是升序

const isAsc = order === 'asc'

let effectiveArr = data.filter((item) => item[property] !== '')

const invalidArr = data.filter((item) => item[property] === '')

effectiveArr = effectiveArr.sort((a, b) => {

if (a[property] > b[property]) {

return isAsc ? 1 : -1

} else {

return isAsc ? -1 : 1

}

})

dataList = effectiveArr.concat(invalidArr)

this.contentsData = dataList

}

},

// 关闭表格弹窗

closePopup() {

this.showLinkTable = false

},

// 关闭点击问号注释弹窗

canclePopup() {

this.showExplain = false

},

// 清除画布

handleClear() {

this.graph && this.graph.destroy()

this.graph = null

this.graphReady = false

},

},

beforeDestroy() {

this.handleClear()

},

}

</script>

 

<style lang="scss" scoped>

.page {

background: #ffffff;

overflow: hidden;

 

.title {

font-size: 32rpx;

text-align: center;

border-bottom: 1px solid #DCDEE3;

 

.nBack {

height: 100%;

display: flex;

align-items: center;

justify-content: center;

}

}

 

.tablePopup {

width: 100%;

background-color: #ffffff;

 

.tableHeader {

width: 100%;

height: 100rpx;

display: flex;

justify-content: space-between;

align-items: center;

border-bottom: 1px solid #DCDEE3;

 

.tableTitle {

font-size: 30rpx;

font-family: PingFang SC;

font-weight: bold;

color: #333333;

margin-left: 30rpx;

width: 80%;

height: 100rpx;

line-height: 100rpx;

white-space: nowrap;

}

 

.tableIcon {

width: 44rpx;

height: 44rpx;

background-color: #F4F4F4;

margin-right: 30rpx;

border-radius: 50%;

display: flex;

justify-content: center;

align-items: center;

 

.icon-shanchu1 {

font-size: 32rpx;

color: #C0C0C0;

 

}

}

}

 

.tableContent {

width: 100%;

height: 800rpx;

margin: 0rpx 0rpx 30rpx;

 

.time {

font-size: 22rpx;

height: 68rpx;

color: #999999;

margin-left: 30rpx;

line-height: 68rpx;

}

 

.lyyTable {

width: 100%;

height: calc(100% - 68rpx) !important;

overflow: hidden;

font-size: 24rpx;

}

}

}

 

.explainPopup {

width: 100%;

background-color: #fff;

 

.explainHeader {

width: 100%;

height: 100rpx;

display: flex;

justify-content: space-between;

align-items: center;

border-bottom: 1px solid #DCDEE3;

 

.explainTitle {

font-size: 30rpx;

font-family: PingFang SC;

font-weight: bold;

color: #333333;

margin-left: 30rpx;

width: 80%;

height: 100rpx;

line-height: 100rpx;

white-space: nowrap;

}

 

.explainIcon {

width: 44rpx;

height: 44rpx;

background-color: #F4F4F4;

margin-right: 30rpx;

border-radius: 50%;

display: flex;

justify-content: center;

align-items: center;

 

.icon-shanchu1 {

font-size: 32rpx;

color: #C0C0C0;

 

}

}

}

 

.explainContent {

padding: 38rpx 57rpx 38rpx 30rpx;

font-size: 24rpx;

color: #666666;

}

}

}

 

/deep/ .uni-table-th {

color: #3849B4;

background-color: #F7F8FB !important;

font-size: 24rpx;

}

 

/deep/ .uni-table-td {

font-size: 24rpx;

}

</style>

storage.js文件获取session方法:   // sessionStorage获取 export function getSession(key) {   return getObjectValue(sessionStorage.getItem(getMixKey(key))) } graphConfig.js文件公共方法:   /** 获取Path贝塞尔曲线控制点坐标  * @param {Number} startX 弧线起始x坐标  * @param {Number} startY 弧线起始Y坐标  * @param {Number} endX 弧线结束X坐标  * @param {Number} endY 弧线结束Y坐标  * @param {Number} angle 弧线角度  */ export function getArcPosition (startX, startY, endX, endY, angle) {   const PI = Math.PI;   // 两点间的x轴夹角弧度   const yOffset = endY - startY   const xOffset = endX - startX   let xAngle = Math.atan2(yOffset, xOffset);   // 转为角度   xAngle = 360 * xAngle / (2 * PI);   // 两点间的长度   const L = Math.sqrt(yOffset * yOffset + xOffset * xOffset);   // 计算等腰三角形斜边长度   const L2 = L / 2 / Math.cos(angle * 2 * PI / 360);   // 求第一个顶点坐标,位于下边   const top = {};   // 求第二个顶点坐标,位于上边   const bottom = {};   top.x = startX + Math.round(L2 * Math.cos((xAngle + angle) * 2 * PI / 360));   top.y = startY + Math.round(L2 * Math.sin((xAngle + angle) * 2 * PI / 360));   bottom.x = startX + Math.round(L2 * Math.cos((xAngle - angle) * 2 * PI / 360));   bottom.y = startY + Math.round(L2 * Math.sin((xAngle - angle) * 2 * PI / 360));   return { top, bottom } } // 折叠图标 const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) { return [ ['M', x - r, y], ['L', x + r, y], ] }   // 展开图标 const EXPAND_ICON = function EXPAND_ICON(x, y, r) { return [ ['M', x - r, y], ['L', x + r, y], ['M', x, y - r], ['L', x, y + r] ] }   // 添加展开/折叠节点(canvas) export function addCollapseNode (group, w, cfg) {   const isLeft = cfg.x < 0   const x = isLeft ? -w / 2 - 11 : w / 2 - 1   const radius = isLeft ? [2, 0, 0, 2] : [0, 2, 2, 0]   group.addShape('rect', {     attrs: {       x,       y: -8,       width: 10,       height: 18,       stroke: '#E5E5E5',       fill: '#EEF2F8',       radius,     },     name: 'marker-box',   });   //+/-   group.addShape('marker', {     attrs: {       x: x + (isLeft ? 6 : 6),       y: 1,       r: 3,       stroke: '#A9ADB7',       lineWidth: 1,       cursor: 'pointer',       symbol: cfg.collapsed ? EXPAND_ICON : COLLAPSE_ICON     },     name: 'collapse-icon',   }) }   // 默认边和箭头样式 export const defaultEdgeStyle = {   stroke: '#3849B4',   endArrow: {     path: 'M 0,0 L 5,4 L 5,-4 Z',     lineWidth: 1,     fill: '#3849B4',   }, }     /**  * 计算节点宽度和高度  * @param {Array} content 要展示的实体内容  * @param {StrNumbering} vPadding 横向内部padding  * @param {Number} hPadding 纵向内部padding  * @param {Number} defaultWidth 默认宽度  * @param {Number} maxWidth  最大宽度  * @param {Number} defaultHeight  默认高度  * @param {Number} maxHeight  最大高度  * @param {Number} otherHeight  其他占用高度  * @returns  */ export function computeWidthHeight ({ content, vPadding = 12, hPadding = 16, defaultWidth = 200, maxWidth = 240, defaultHeight = 50, maxHeight = 160, otherHeight = 34 }) {   let rowNum = content.length   let width = defaultWidth   let defaultLength = Math.floor((width - 2 - vPadding) / 12)   let res = []   // 判断150宽度能否满足每一行数据要求   const isDissatisfy = content.some((item) => computeStrOccupyLength(item) > defaultLength)   // 如果不满足则切割字符串   if (isDissatisfy) {     width = maxWidth     defaultLength = Math.floor((width - 2 - vPadding) / 12)     for (let i = 0, l = content.length; i < l; i++) {       let item = content[i]       const matchItems = splitStr(item, defaultLength)       rowNum += (matchItems.length - 1)       res.push(...matchItems)     }   } else {     res = content   }   let height = Math.max(defaultHeight, rowNum * 14 + hPadding)   height = Math.min(height, maxHeight) + otherHeight   return {     width,     height,     content: res   } }   // 计算字符串占用长度 function computeStrOccupyLength (str) {   let num = 0   str.split('').forEach((s) => {     if (/[0123456789.?()-:]/.test(s)) {       num += 0.5     } else {       num += 1     }   })   return Math.ceil(num) }   // 根据数量截取字符串 function splitStr (str, maxNum) {   let num = maxNum   const res = []   let tmp = []   str.split('').forEach((s, i) => {     tmp.push(s)     if (/[0123456789.?()-:]/.test(s)) {       num -= 0.5     } else {       num -= 1     }     if (num < 1 || i === str.length - 1) {       res.push(tmp.join(''))       tmp = []       num = maxNum     }   })   return res }   // 处理添加子节点 export function handleChildrenData (data, level) {   const res = [];   data.forEach((item) => {     const { children, name, parentId, nodeInterpret } = item;     children.forEach((subItem, index) => {       let content = subItem.name;       if (content) {         const { showType, isChildren } = subItem;         const id = `${subItem.id}_${index}`;         content = content.split('\n').filter((item) => item);         res.push({           id,           parentId,           content,           name,           nodeInterpret,           showType,           level,           collapsed: true,           hasChildren: Boolean(isChildren),         });       }     });   });   return res; }  

表格插件文件:

去插件市场下载表格插件:

我这里改了排序,数据为空默认展示,列点击事件源代码,直接复制就好:

<template>

<view :style="{height}" :prop="ready" :change:prop="tableRender.documentReady">

<scroll-view scroll-x scroll-y class="container" @scrolltolower="scrolltolower">

<uni-table stripe border :loading="loading" style="min-height: 100%;">

<uni-tr id="lyy-thead" :class="headerFixed?'fixed-head':''" style="display: flex;">

<uni-th v-for="(item,index) in headers" :key="index" :width="item.width" align="center" :style="{

          display:item.hidden?'none':'flex',width:item.width,justifyContent: 'center',

                  left:columnFixed>0?fixedLeft(index):'unset',

                  right:columnFixed<0?fixedRight(index):'unset',

                  position:isFixed(index)?'sticky':'unset',

                  borderLeft:columnFixed<0&&isFixed(index)?'1px solid #f0f0f0':'unset',

                  zIndex:index<=columnFixed-1?999:99,

                  backgroundColor:'inherit'

          }">

<view @click="doSort(item)" style="display: flex;flex-direction: row;justify-content: center;">

<text :style="{lineHeight:'20px'}">{{item.label}}</text>

<view class="header-icon"

style="line-height:6px;display:flex;flex-direction:column;margin-left:5px;justify-content: center;"

v-if="item.sort">

<text class="iconfont icon-arrow-up"

:style="{color:lastSortItem===item.key&&sortWay=='asc'?'#3849B4':'#bcbcbc'}" />

 

<text class="iconfont icon-arrow-down"

:style="{color:lastSortItem===item.key&&sortWay=='desc'?'#3849B4':'#bcbcbc'}" />

</view>

 

</view>

</uni-th>

</uni-tr>

<!--<uni-tr v-if="headerFixed" :style="{height: theadHeight}">

            </uni-tr>

            <!-- <uni-td class="no_data" align="center">暂无数据</uni-td> -->

<view v-if="contents.length<1" class="no_data">暂无数据</view>

<uni-tr v-else v-for="(content,sindex) in sortContents" :key="sindex"

style="display:flex;table-layout: fixed;min-width: 100%;" @click.native="getRow(content)">

<!-- @click.native="rowClick(content)">-->

<uni-td v-for="(header,hindex) in headers" class="tableCell" :data-wrap="overflow" :key="hindex"

align="center" :style="{display:header.hidden?'none':'flex',textAlign:'center',width:header.width,

                  left:columnFixed>0?fixedLeft(hindex):'unset',

                  right:columnFixed<0?fixedRight(hindex):'unset',

  justifyContent:'center',

  alignItems:'center',

                  position:isFixed(hindex)?'sticky':'unset',

                  borderLeft:columnFixed<0&&isFixed(hindex)?'1px solid #f0f0f0':'unset',

                  zIndex:hindex<=columnFixed-1?9:'unset',

                  backgroundColor:'inherit',

  overflow:'hidden'}">

<template v-if="header.format!==undefined">

<lyy-progress

v-if="header.format.type==='progress'&&!isNaN(parseFloat(content[header.key]))"

:percent="content[header.key].toFixed(2)" show-info round></lyy-progress>

<view v-else-if="header.format.type==='html'" v-html="content[header.key]"></view>

<text v-else>{{content[header.key]}}</text>

</template>

<text v-else>{{content[header.key]}}</text>

</uni-td>

</uni-tr>

<uni-tr v-if="contents.length>0&&totalRow.length>0" style="min-width: 100%;display: flex;" @click.native="getRow(content)">

<uni-td v-for="(header,index) in headers" :key="Math.random()" align="center" :style="{textAlign: 'center',display:header.hidden?'none':'table-cell',width:header.width,

                  left:columnFixed>0?fixedLeft(index):'unset',

                  right:columnFixed<0?fixedRight(index):'unset',

                  position:isFixed(index)?'sticky':'unset',

                  borderLeft:columnFixed<0&&isFixed(index)?'1px solid #f0f0f0':'unset',

                  zIndex:index<=columnFixed-1?9:'unset',

                  backgroundColor:'inherit'}">

<text v-if="index==0">合计</text>

<view v-else>

<!--<progress v-if="typeof header.format!=='undefined'&& header.format.type==='progress'" :percent="renderTotalRow(header)" :show-info="true" stroke-width="10" :active="true"></progress>-->

<lyy-progress

v-if="typeof header.format!=='undefined'&& header.format.type==='progress'&&!isNaN(parseFloat(renderTotalRow(header)))"

:percent="renderTotalRow(header)" :show-info="true" round></lyy-progress>

<text v-else>{{ renderTotalRow(header)}}</text>

</view>

</uni-td>

</uni-tr>

</uni-table>

<!-- <uni-load-more v-show="showLoadMore" :status="loadMore"></uni-load-more> -->

</scroll-view>

</view>

</template>

 

<script>

import lyyProgress from './lyy-progress'

/**

* lyyTable ver1.3.8

* @description lyyTable表格组件 ver1.3.8

*/

export default {

name: "lyyTable",

components: {

lyyProgress

},

data() {

return {

ready: 1,

lastSortItem: '', //上一次排序列

sortWay: 'none', //默认无排序

sortIndex: 0,

sortContents: [], //排序时的表格内容

footContent: {},

scrollHeight: '',

theadHeight: ''

}

},

props: {

//表格高度 1.3.8

// #ifdef H5

height: {

type: String,

default: 'calc(100vh - 44px - env(safe-area-inset-top))'

},

// #endif

// #ifndef H5

height: {

type: String,

default: '100vh'

},

// #endif

overflow: {

type: String,

default: 'nowrap',

validator: val => ['wrap', 'nowrap'].indexOf(val) > -1

},

//显示加载

loading: {

type: Boolean,

default: false

},

//上拉加载文字,参考uni-load-more

loadMore: {

type: String,

default: 'more'

},

//是否显示上拉加载组件

showLoadMore: {

type: Boolean,

default: false

},

//固定表头

headerFixed: {

type: Boolean,

default: false

},

//固定首列 ver1.3.3弃用

/*firstColumnFixed: {

    type: Boolean,

    default: false

},*/

//固定列数 ver1.3.3新增

columnFixed: {

type: Number,

default: 0

},

//排序方式

sortWays: {

type: Array,

default: () => ['none', 'asc', 'desc']

},

//数据为空时的占位符

emptyString: {

type: String,

default: '-'

},

//表头

headers: {

type: Array,

default: () => [],

},

//表格数据

contents: {

type: Array,

default: () => []

},

//合计列

totalRow: {

type: Array,

default: () => []

}

},

mounted() {

//uni.setStorageSync('contents',this.contents)

this.sortContents = JSON.parse(JSON.stringify(this.contents))

this.renderContents()

this.createTotalRow()

if (this.overflow == 'nowrap') {

this.ready = this.headers.length * this.contents.length

}

//ver 1.2.0 修复 uni-table width 问题

/*this.$nextTick(() => {

    console.log('abc',this.$refs)

    const query = uni.createSelectorQuery().in(this)

    query.select('.uni-table').boundingClientRect(dom=>{

        console.log(123456,dom)

    }).exec()

    this.$refs['uni-table'].removeAttribute('style')

})*/

//ver 1.2.0 新增 固定表头

/*if (this.headerFixed) {

    /*var wHeight = document.body.clientHeight

    var tablePoseY = document.getElementById('lyy-tbody').getBoundingClientRect().y

    document.getElementById('lyy-tbody').style.height = wHeight - tablePoseY + 'px'

    const query = uni.createSelectorQuery().in(this)

 

    query.select('#lyy-thead').boundingClientRect(dom => {

        console.log(dom)

        this.theadHeight = dom.height + 'px'

    }).exec()

}*/

},

watch: {

contents: {

//console.log(value)

handler(value) {

this.sortContents = JSON.parse(JSON.stringify(value))

console.log('len--------', value.length)

this.renderContents()

this.createTotalRow()

this.lastSortItem = ''

this.sortWay = 'none'

for (var header of this.headers) {

this.renderTotalRow(header)

}

//this.$forceUpdate()

this.$nextTick(function() {

if (this.overflow == 'nowrap') {

this.ready = this.headers.length * this.contents.length

}

})

},

deep: true

 

},

//监听排序变化

sortChange(value) {

var that = this

var contents = JSON.parse(JSON.stringify(that.contents))

switch (value.sortWay) {

case 'none':

that.sortContents = contents

this.renderContents()

break

case 'asc': //正序

that.sortContents = that.sortContents.sort(function(a, b) {

//需要排序的列为数字时直接计算

if (!isNaN(Number(a[that.lastSortItem])) && !isNaN(Number(b[that

.lastSortItem]))) {

if (a[that.lastSortItem] == '' || b[that.lastSortItem] == ''){

return b[that.lastSortItem] - a[that.lastSortItem]

}else {

return a[that.lastSortItem] - b[that.lastSortItem]

}

 

}

//非数字转为ASCII排序(1.3.7弃用)

//1.3.7更改排序方式,使中文排序更符合中国习惯

else {

//(1.3.7弃用) return a[that.lastSortItem].charCodeAt() - b[that.lastSortItem].charCodeAt()

return a[that.lastSortItem].localeCompare(b[that.lastSortItem], 'zh-cn')

}

})

break

case 'desc': //倒序

that.sortContents = that.sortContents.sort(function(a, b) {

if (!isNaN(Number(a[that.lastSortItem])) && !isNaN(Number(b[that

.lastSortItem]))) {

return b[that.lastSortItem] - a[that.lastSortItem]

}

//非数字转为ASCII排序(1.3.7弃用)

//1.3.7更改排序方式,使中文排序更符合中国习惯

else {

//(1.3.7弃用) return b[that.lastSortItem].charCodeAt() - a[that.lastSortItem].charCodeAt()

return b[that.lastSortItem].localeCompare(a[that.lastSortItem], 'zh-cn')

}

})

break

}

that.$forceUpdate()

}

},

computed: {

//将排序方式、上次排序列作为一个整体进行监听,不然会出现切换排序列不排序的现象

sortChange() {

var {

sortWay,

lastSortItem

} = this

return {

sortWay,

lastSortItem

}

}

},

methods: {

//点击排序表头时存储上次排序列名,并循环切换排序方式

doSort(item) {

if (item.sort) {

if (this.lastSortItem !== item.key) {

this.lastSortItem = item.key

this.sortIndex = 0

this.sortIndex++

this.sortWay = this.sortWays[this.sortIndex]

} else {

if (this.sortIndex < 2) {

this.sortIndex++

this.sortWay = this.sortWays[this.sortIndex]

} else {

this.sortIndex = 0

this.sortWay = this.sortWays[0]

}

}

}

},

// 表格行点击事件

getRow(data) {

this.$emit("row-click",data)

},

//表格内容渲染

renderContents() {

const headers = this.headers

//防止修改穿透

var contents = JSON.parse(JSON.stringify(this.contents))

//var contents=uni.getStorageSync('contents')

let sortContents = JSON.parse(JSON.stringify(this.sortContents))

var newArr = []

var result = ''

sortContents.forEach(function(content, index) {

var item = content

headers.forEach(function(header) {

//字符类型格式化

if (typeof header.format !== 'undefined' && (header.format.type === 'string' ||

header.format.type === 'html')) {

var template = header.format.template

var keys = header.format.keys

//console.log(typeof template)

if (typeof template === 'function') {

var arg = []

keys.forEach((el, i) => {

arg.push(contents[index][el])

})

result = template(arg)

//console.log(result)

} else {

keys.forEach((el, i) => {

var value = contents[index][el]

var reg = new RegExp('\\{' + i + '}', 'g')

template = template.replace(reg, value)

})

result = template

}

item[header.key] = result

}

//计算类型格式化

else if (typeof header.format !== 'undefined' && (header.format.type ===

'compute' || header.format.type === 'progress')) {

//console.log(header.format.template)

var temp = header.format.template

var keys = header.format.keys

//1.3.7 使计算列支持function

if (typeof temp === 'function') {

var arg = []

keys.forEach((el, i) => {

arg.push(contents[index][el])

})

item[header.key] = temp(arg)

//console.log(result)

} else {

keys.forEach((el, i) => {

var reg = new RegExp('\\{' + i + '}', 'g')

temp = temp.replace(reg, contents[index][el])

})

//console.log(temp)

item[header.key] = eval(temp)

//this.sortContents[index][header.key]=result

}

}

})

newArr.push(item)

})

this.sortContents = newArr

},

createTotalRow() {

if (this.totalRow.length > 0 && this.sortContents[0] !== undefined) {

/*var obj = {...this.contents[0]}

console.log(obj)

for (var i in obj) {

    this.footContent[i] = obj[i]

}*/

this.footContent = JSON.parse(JSON.stringify(this.sortContents[0]))

for (var i in this.footContent) {

var result = 0

if (this.sortContents.length > 0) {

for (var j in this.sortContents) {

result += parseFloat(this.sortContents[j][i]) || 0

}

}

this.footContent[i] = result

}

}

},

//合计列渲染

renderTotalRow(header) {

var content = JSON.parse(JSON.stringify(this.footContent))

var result = this.emptyString

if (this.totalRow.indexOf(header.key) > -1) {

if (typeof header.format !== 'undefined' && header.format.type === 'progress') {

var temp = header.format.template

var keys = header.format.keys

for (var index in keys) {

var reg = new RegExp('\\{' + index + '}', 'g')

temp = temp.replace(reg, content[keys[index]])

}

result = eval(temp)

result = isNaN(result) ? 0 : result.toFixed(2)

} else {

if (content[header.key] != null && !isNaN(content[header.key])) {

result = content[header.key]

}

}

}

return result

},

//行点击事件

rowClick(data) {

this.$emit('rowClick', data)

},

//上拉加载事件

scrolltolower(e) {

if (e.detail.direction == 'bottom') {

this.$emit('onPullup')

}

},

//固定列left计算

fixedLeft(index) {

var headers = this.headers.filter(item => !item.hidden)

var left = 'calc(1px'

for (var i = 1; i < index + 1; i++) {

left += ' + ' + headers[i - 1].width

}

left += ')'

return left

},

//v1.3.5

//固定列right计算

fixedRight(index) {

var headers = this.headers.filter(item => !item.hidden)

var columnFixed = Math.abs(this.columnFixed)

if (index >= headers.length + this.columnFixed) {

var right = 'calc(1px'

for (var i = index; i < headers.length - 1; i++) {

if (index + 1 == headers.length) {

break

} else {

right += ' + ' + headers[i + 1].width

}

}

right += ')'

return right

} else {

return 'unset'

}

},

//v1.3.5

isFixed(index) {

if (this.columnFixed > 0) {

return index <= this.columnFixed - 1

} else if (this.columnFixed < 0) {

var headers = this.headers.filter(item => !item.hidden)

return index >= headers.length + this.columnFixed

} else {

return false

}

}

}

}

</script>

<script module="tableRender" lang="renderjs">

function test(a, b) {

alert(a, b)

}

export default {

methods: {

documentReady(newValue, oldValue, ownerInstance, instance) {

//alert(`${newValue},${oldValue}`)

console.log(newValue, oldValue)

if (newValue > oldValue) {

var cells = document.querySelectorAll('.tableCell')

 

function changeWrap(e) {

console.log(1)

var wrap = e.currentTarget.dataset.wrap

e.currentTarget.dataset.wrap = wrap == 'nowrap' ? 'unset' : 'nowrap'

}

 

function fun(e) {

changeWrap(e)

}

// for (var i = oldValue - 1; i < cells.length; i++) {

// cells[i].addEventListener('click', fun)

// }

}

}

}

}

</script>

<style>

/deep/.uni-table-loading {

display: none !important;

}

</style>

 

<style scoped>

@import './css/iconfont.css';

 

.container {

width: 100vw;

height: 100%;

border-bottom: 1px solid #f0f0f0;

}

 

.uni-table-scroll {

overflow: unset !important;

border-top: none;

}

 

.no_data {

position: fixed;

width: 750upx;

background-color: #FFF;

height: 50px;

text-align: center;

line-height: 50px;

}

 

#lyy-thead {

/*min-width: 750upx;*/

display: table;

background-color: #FFF;

}

 

[data-wrap='unset'] {

white-space: unset !important;

display: flex !important;

}

 

[data-wrap='nowrap'] {

white-space: nowrap !important;

}

[data-wrap='nowrap'] uni-text{

width: 100%;

text-overflow: ellipsis;

overflow: hidden;

}

 

.fixed-head {

position: sticky;

top: 0;

z-index: 99;

border-top: 1px solid #f0f0f0;

}

 

/*修复滑动时偏移1px问题*/

/*.table--border {

border-top: none;

border-left: none;

}*/

 

.uni-table-tr {

background-color: #FFF;

}

</style>

排序字体图标需要缩小,在css文件中iconfont.css文件里面改字体图标样式即可: .iconfont {   font-family: "iconfont" !important;   font-size: 12px !important;   transform: scale(0.7); // 图标缩小,根据需求自己定义缩小值,因浏览器默认字体是12px,设置即可小于12px   font-style: normal;   -webkit-font-smoothing: antialiased;   -moz-osx-font-smoothing: grayscale; }  

标签:uniapp,插件,const,item,uView,content,width,var,return
来源: https://www.cnblogs.com/xiaofang234/p/16408159.html