Uniapp Vue Table上下左右滚动

提取组件前 <template> <view class="container"> <view class="table-wrap"> <view class="table-content"> <view class="tr header-row"> <view v-for="(col, index) in columns" :key="index" class="th" :class="{ 'fixed-col': col.fixed, 'stretch-col': index === columns.length - 1 && !col.fixed }" :style="{ width: col.width, left: col.fixed ? '0' : 'auto' }" > {{ col.label }} </view> </view> <view class="tr body-row" v-for="(row, rowIndex) in tableData" :key="rowIndex"> <view v-for="(col, colIndex) in columns" :key="colIndex" class="td" :class="{ 'fixed-col': col.fixed, 'stretch-col': colIndex === columns.length - 1 && !col.fixed }" :style="{ width: col.width, left: col.fixed ? '0' : 'auto' }" > {{ row[col.prop] }} </view> </view> </view> </view> </view> </template> <script> export default { data() { return { columns: [ { label: '编号', prop: 'id', width: '150rpx', fixed: true }, { label: '姓名', prop: 'name', width: '200rpx' }, { label: '年龄', prop: 'age', width: '150rpx' }, { label: '部门', prop: 'dept', width: '200rpx' }, { label: '职位', prop: 'job', width: '200rpx' }, { label: '入职时间', prop: 'date', width: '260rpx' }, { label: '备注信息', prop: 'remark', width: '400rpx' } ], tableData: [] }; }, mounted() { this.loadData(); }, methods: { loadData() { const list = []; for (let i = 1; i <= 20; i++) { list.push({ id: `100${i}`, name: `测试用户${i}`, age: 20 + i, dept: '研发中心', job: '前端工程师', date: '2023-11-01', remark: i % 3 === 0 ? '这是一个非常长非常长非常长非常长非常长非常长非常长的备注文本,用于测试单元格的自动换行功能和固定列的自动撑高对齐效果。' : '测试文本较短。' }); } this.tableData = list; } } }; </script> <style scoped> .container { height: 100vh; background-color: #fff; display: flex; flex-direction: column; } .table-wrap { flex: 1; height: 0; overflow: auto; position: relative; } .table-content { display: inline-block; min-width: 100%; } .tr { display: flex; width: 100%; white-space: normal; align-items: stretch; } .th, .td { display: flex; align-items: center; justify-content: flex-start; text-align: left; box-sizing: border-box; padding: 10rpx; border-right: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8; flex-shrink: 0; font-size: 28rpx; background-color: #fff; line-height: 1.4; } .stretch-col { flex-grow: 1; width: 0 !important; min-width: 150rpx; } .header-row { position: sticky; top: 0; z-index: 10; height: 80rpx; background-color: #dcdfe6; } .th { background-color: #ebeef5; font-weight: bold; color: #606266; border-bottom: 1px solid #ebeef5; height: 100%; } .fixed-col { position: sticky; left: 0; z-index: 5; border-right: 1px solid #dcdfe6; } .header-row .fixed-col { z-index: 20; } .body-row:hover .td { background-color: #f5f7fa; } </style> 提取组件 <template> <view class="container"> <view class="table-wrap"> <view class="table-content"> <view class="tr header-row"> <view v-for="(col, index) in columns" :key="index" class="th" :class="{ 'fixed-col': col.fixed, 'stretch-col': index === columns.length - 1 && !col.fixed }" :style="{ width: col.width, left: col.fixed ? '0' : 'auto' }" > {{ col.label }} </view> </view> <view class="tr body-row" v-for="(row, rowIndex) in tableData" :key="rowIndex"> <view v-for="(col, colIndex) in columns" :key="colIndex" class="td" :class="{ 'fixed-col': col.fixed, 'stretch-col': colIndex === columns.length - 1 && !col.fixed }" :style="{ width: col.width, left: col.fixed ? '0' : 'auto' }" > {{ row[col.prop] }} </view> </view> </view> </view> </view> </template> <script> export default { props: { /** * label:列名称, * prop: 对应 tableData 中数据的键名 (key),用于内容映射 * width: 宽度 * fixed: 是否将该列固定在左侧。目前组件仅支持固定最左侧的第一列。 */ columns: { type: Array, required: true, default: () => [] }, tableData: { type: Array, required: true, default: () => [] } } }; </script> <style scoped> .container { height: 100%; background-color: #fff; display: flex; flex-direction: column; } .table-wrap { flex: 1; height: 0; overflow: auto; position: relative; } .table-content { display: inline-block; min-width: 100%; } .tr { display: flex; width: 100%; white-space: normal; align-items: stretch; } .th, .td { display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; padding: 10rpx; border-right: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8; flex-shrink: 0; font-size: 28rpx; background-color: #fff; line-height: 1.4; } .stretch-col { flex-grow: 1; min-width: 150rpx; } .header-row { position: sticky; top: 0; z-index: 10; height: 80rpx; } .th { background-color: #ebeef5; font-weight: bold; color: #606266; border-bottom: 1px solid #ebeef5; height: 100%; } .fixed-col { position: sticky; left: 0; z-index: 5; border-right: 1px solid #dcdfe6; } .header-row .fixed-col { z-index: 20; } .body-row:hover .td { background-color: #f5f7fa; } </style>

2025年12月01日

Android退出APP拦截

import android.app.AlertDialog import android.os.Bundle import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import androidx.activity.viewModels import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() private val TAG = "MainActivity" private val backCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { handleAppExit() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) onBackPressedDispatcher.addCallback(this, backCallback) } private fun handleAppExit() { backCallback.isEnabled = false lifecycleScope.launch(Dispatchers.IO) { try { //判断是否需要提示 val isBeConfirm = true if (isBeConfirm) { withContext(Dispatchers.Main) { showExitConfirmationDialog() } } else { withContext(Dispatchers.Main) { finish() } } } catch (e: Exception) { Timber.tag(TAG).e(e) if (e !is CancellationException) { withContext(Dispatchers.Main) { finish() } } } finally { withContext(Dispatchers.Main) { backCallback.isEnabled = true } } } } private fun showExitConfirmationDialog() { AlertDialog.Builder(this) .setTitle("还不能退出") .setMessage("退出后,会如何?") .setPositiveButton("继续退出") { dialog, which -> finish() } .setNegativeButton("取消") { dialog, which -> dialog.dismiss() } .setCancelable(false) .show() } }

2025年11月28日

微信小程序-分包

微信小程序-分包 分包后-分包内的JS文件还是会被打包入主包的vendor.js,导致超出2M限制 项目根目录创建vue.config.js module.exports = { configureWebpack: { optimization: { splitChunks: { cacheGroups: { subPackageA: { name: 'subPackageName/vendor', test: /[\\/]node_modules[\\/](library-name)[\\/]/, chunks: 'all', priority: 10, enforce: true } } } } } }; manifest.json 增加配置 "mp-weixin" : { "optimization" : { "subPackages" : true } },

2025年11月17日

Android 蓝牙连接

Android 蓝牙连接 RxAndroidBle连接库只能扫描出Ble类型设备 使用原生Android扫描出设备获取Mac地址,然后用RxAndroidBle连接 fun scanBleDeviceNative(deviceName: String) { val adapter = (context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager).adapter if (adapter == null || !adapter.isEnabled) { Timber.tag(TAG).w("蓝牙未开启") return } // 如果之前正在扫描,先取消 if (adapter.isDiscovering) adapter.cancelDiscovery() // 监听经典蓝牙发现广播 val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { BluetoothDevice.ACTION_FOUND -> { val device: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java ) } else { @Suppress("DEPRECATION") intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) } device?.let { val name = it.name ?: return if (name.contains(deviceName, ignoreCase = true)) { Timber.tag(TAG) .d("找到设备: $name ${device.address}") // 将经典 BluetoothDevice 转为 RxBleDevice 对象 val bleDevice = rxBleClient.getBleDevice(device.address) // 连接 到设备.... // 停止扫描 try { adapter.cancelDiscovery() } catch (_: Exception) { } context.unregisterReceiver(this) } } } BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> { Timber.tag(TAG).d("扫描结束") context.unregisterReceiver(this) } } } } val filter = IntentFilter(BluetoothDevice.ACTION_FOUND).apply { addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) } context.registerReceiver(receiver, filter) // 启动蓝牙扫描 adapter.startDiscovery() Timber.tag(TAG).d("开始扫描") // 设置 10 秒超时停止扫描 Handler(Looper.getMainLooper()).postDelayed({ try { if (adapter.isDiscovering) adapter.cancelDiscovery() Timber.tag(TAG).d("扫描超时,已停止") context.unregisterReceiver(receiver) } catch (_: Exception) { } }, 10_000) }

2025年11月07日

数学求和符号

求和符号 ($\sum$) 的含义 这个公式片段的完整形式通常是这样的: $$ \sum_{i=1}^{N} f(i) $$ 符号 含义 $\sum$ (Sigma) 求和符号。表示将一系列数值加起来(累加)。 $i=1$ 起始索引。表示求和从 $i$ 等于 1 开始。 $N$ 结束索引。表示求和到 $i$ 等于 $N$ 结束。 $f(i)$ 求和项。表示每次累加的具体数值或表达式。 用通俗的话来说,它的意思是: “将 $f(i)$ 这个表达式,从 $i=1$ 开始,一直算到 $i=N$ 结束,然后把所有结果加起来。” 示例 如果 $N=4$,且求和项是 $f(i) = i^2$,那么公式的展开式就是: $$ \sum_{i=1}^{4} i^2 = 1^2 + 2^2 + 3^2 + 4^2 = 1 + 4 + 9 + 16 = 30 $$

2025年10月22日

Scp上传文件

scp -P 22 /path/file sshuser@hostname:/path -P 大写P,后是ssh端口 需要传到服务器的文件路径 sshuser: 服务器ssh用户 hostname: 服务器IP

2025年09月29日

启用长路径

Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled (Type: REG_DWORD) 设置为1

2025年09月29日

Vim 基础配置

" 语法高亮 syntax enable syntax on set t_Co=256 " 背景 set background=dark "colorscheme dark set termguicolors set backspace=indent,eol,start set clipboard=unnamed set mouse=a set title set autoread set history=1000 set formatoptions+=j "set shell set tabstop=4 set expandtab set shiftwidth=4 set softtabstop=4 set modelines=1 filetype plugin on filetype indent on set autoindent set shiftround set smarttab set number set showcmd set showmatch set cursorline set wildmenu set lazyredraw set complete-=i "set fillchars+=vert:| set laststatus=2 set ruler set display+=lastline set encoding=utf-8 set linebreak "set scrolloff set sidescrolloff=5 set wrap set ignorecase set incsearch set hlsearch set smartcase set noerrorbells set foldmethod=indent set foldnestmax=10 set foldenable nnoremap <space> za set foldlevelstart=10 set nobackup set noswapfile

2025年09月29日

PyenvWindows安装配置

PyenvWindows安装配置 1.Power Shell脚本安装pyenv-win Invoke-WebRequest -UseBasicParsing -Uri https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/install-pyenv-win.ps1 -OutFile install-pyenv-win.ps1 Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser .\install-pyenv-win.ps1 2.重新打开PowerShell 设置国内源环境变量 [Environment]::SetEnvironmentVariable('PYTHON_BUILD_MIRROR_URL','https://mirrors.huaweicloud.com/python/','User') 更新源 pyenv update 3.安装python pyenv install 3.10.11

2025年09月27日

Win10安装MySQl8+服务

Win10安装MySQl8+服务 1.软件路径如D:\soft\mysql-8.4 2.根目录配置my.ini [mysqld] # 设置端口 同一机器设置不同端口 port = 3306 # 设置mysql的安装目录 basedir= D:/soft/mysql-8.4 # 设置mysql数据库的数据的存放目录 datadir= D:/soft/mysql-8.4/data # 允许最大连接数 max_connections=20 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB mysql_native_password=ON log-bin=mysql-bin gtid-mode=on log_replica_updates=true enforce-gtid-consistency=true sync_binlog=0 binlog-checksum=CRC32 source_verify_checksum=1 replica_sql_verify_checksum=1 binlog-rows-query-log_events=1 sync_source_info=1 #expire_logs_days=5 max_binlog_size=1024M # 主从服务ID主=1 其他递增 server-id=1 # #masterA自增长ID #奇数ID #偶数ID auto_increment_offset = 1 auto_increment_increment = 2 replicate-ignore-db = mysql replicate-ignore-db = information_schema replicate-ignore-db = performance_schema replicate-ignore-db = sys #skip-character-set-client-handshake #忽略应用程序想要设置的其他字符集 init-connect='SET NAMES UTF8MB4' #连接时执行的SQL character-set-server=UTF8MB4 #服务端默认字符集 wait_timeout=1800 #请求的最大连接时间 interactive_timeout=1800 #和上一参数同时修改才会生效 #sql_mode=STRICT_TRANS_TABLES #sql模式 max_allowed_packet = 10M bulk_insert_buffer_size = 8M #skip-name-resolve slow_query_log=1 long_query_time = 6 slow_query_log_file=slow-query.log innodb_flush_log_at_trx_commit = 2 innodb_log_buffer_size = 16M [myisamchk] key_buffer_size = 20M sort_buffer_size = 20M read_buffer = 2M write_buffer = 2M [mysqlhotcopy] interactive-timeout [mysqldump] quick max_allowed_packet = 16M 3.安装脚本install.bat @echo off :: MySQL 安装目录 set MYSQL_HOME=D:/soft/mysql-8.4 set DATADIR=%MYSQL_HOME%/data echo 停止旧服务(如果存在)... sc query MySQL84 >nul 2>nul if %errorlevel%==0 ( net stop MySQL84 sc delete MySQL84 ) echo 删除旧的数据目录... rmdir /S /Q "%DATADIR%" mkdir "%DATADIR%" echo 初始化 MySQL 数据库(无密码)... "%MYSQL_HOME%/bin/mysqld.exe" --initialize-insecure --basedir="%MYSQL_HOME%" --datadir="%DATADIR%" echo 安装 MySQL 服务 (MySQL84)... "%MYSQL_HOME%/bin/mysqld.exe" --install MySQL84 echo 启动 MySQL 服务... net start MySQL84 echo 设置 root 密码为 123456... "%MYSQL_HOME%/bin/mysql.exe" -u root --execute="ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;" echo MySQL 8.4 安装完成,root 密码已设置为 123456 pause

2025年09月27日