目录
1、引入sqlite模块
2、sqlite文件结构
3、初始化文件index.js
4、打开数据库
5、查询数据
6、可视化测试
SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。
就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。
1、引入sqlite模块
参考上一篇文章具体操作使用uni-app构建android项目-CSDN博客
后续的操作成功后,会在手机磁盘的根路径下生成transfer【数据库实例名】文件夹
里面包含transfer.db、transfer.db-shm、transfer.db-wal
其中transfer.db 可以通过navicat等工具打开
2、sqlite文件结构
在项目根路径下定义db\sqlite文件夹
除了index.js属于初始化sqlite库文件,其余文件均表示对数据库相关表的操作
以boxDetail.js文件为例
在SysBoxDetail 函数内 init 方法,需要检查数据库表是否建立。
以及在已安装的应用程序后,对应的新增字段、索引、表数据修改等升级操作
提供对于表数据查询操作的相关函数
import {sqlite} from '@/db/sqlite/index.js'
import {strUtil, dateUtils} from '@/common/util.js'
const tabName = "sys_box_detail";
/**
* boxNo=箱号,storeroom=库房号,totalNum=已清点数量,
* status=是否封箱 0=否1=是,isDel=是否删除0=否,1=是
*/
const fields = "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,batchId INTEGER not null, "
+" boxId INTEGER, boxNo INTEGER not null, archNo text not null, createTime text,"
+" fileNum text not null, storeroom text"
// 类似于构造函数,必须定义
function BoxDetail(){
}
BoxDetail.prototype = {
constructor: BoxDetail,
}
export const SysBoxDetail = {
init: function(){
if(!sqlite.isOpen()){
sqlite.openDb();
}
return sqlite.isTable(tabName).then((res) => {
console.log(tabName+"是否存在:" + res)
if(!res) {
sqlite.createTab(tabName, fields)
} else {
// 用于后续升级版本 新增 fileNum字段
sqlite.existsField(tabName, 'fileNum').then(res => {
if(res[0].num == 0){
let sql = "ALTER TABLE "+tabName+" ADD COLUMN fileNum text DEFAULT 1"
sqlite.updateBySQL(sql).then(res => {
console.log(res)
console.log("插入字段成功")
})
}
})
// 新增 createTime字段
sqlite.existsField(tabName, 'createTime').then(res => {
if(res[0].num == 0){
let sql = "ALTER TABLE "+tabName+" ADD COLUMN createTime text"
sqlite.updateBySQL(sql).then(res => {
console.log(res)
console.log("插入字段成功")
})
}
})
}
// 新增索引
sqlite.existsIndex(tabName, tabName + '_idx_archNo').then( (res) => {
console.log('索引判断:',res)
if(res[0].num == 0){
sqlite.createIndex(tabName, tabName + '_idx_archNo', 'archNo')
sqlite.createIndex(tabName, tabName + '_idx_boxId', 'boxId')
sqlite.createIndex(tabName, tabName + '_idx_batchId', 'batchId')
sqlite.createIndex(tabName, tabName + '_idx_boxNo', 'boxNo')
}
})
// 移除不存在的批次数据 ,后期再有新版本可以删除该行
this.deleteByNotExistsBatchId();
})
},
insert: function(data){
return sqlite.addTabItem(tabName, data);
},
count: function(queryParam){
let sql = "select count(*) as num from " + tabName + this.where(queryParam);
return sqlite.selectBySQL(sql);
},
selectAll: function(queryParam){
let sql = "select * from " + tabName + this.where(queryParam);
let order = " order by archNo asc ";
return sqlite.selectBySQL(sql + order);
},
selectById: function(id){
let sql = `select * from ${tabName} where id = ${id}`;
return sqlite.selectBySQL(sql);
},
updateFileNumById: function(id, fileNum) {
let sql = `update ${tabName} set fileNum = ${fileNum} where id = ${id}`;
return sqlite.updateBySQL(sql);
},
deleteById: function(id){
return sqlite.deleteInformationType(tabName, {'id': id});
},
deleteByBoxId: function(boxId) {
return sqlite.deleteInformationType(tabName, {'boxId': boxId});
},
deleteByBatchId: function(batchId) {
return sqlite.deleteInformationType(tabName, {'batchId': batchId});
},
/**
* 移除无效的批次数据
*/
deleteByNotExistsBatchId: function(){
let sql = `delete from ${tabName} where batchId not in (select id from sys_batch )`;
return sqlite.updateBySQL(sql);
},
select: function(queryParam){
let sql = "select * from " + tabName + this.where(queryParam);
let order = " order by boxNo, archNo asc ";
let limit = this.limit(queryParam)
return sqlite.selectBySQL(sql + order + limit);
},
/**
* 关联boxId 查询,显示是否封箱
* @param {Object} queryParam
*/
selectWithBox: function(queryParam){
let sql = "select a.*,b.status from " + tabName + " as a left outer join sys_box as b on a.boxId = b.id "
let where = this.where(queryParam, 'a');
let order = " order by a.boxNo, a.archNo asc ";
let limit = this.limit(queryParam)
return sqlite.selectBySQL(sql + where + order + limit);
},
countByBatchNo: function(batchNo){
let sql = "select count(*) as num from " + tabName + this.where({batchNo: batchNo});
return sqlite.selectBySQL(sql);
},
countByArchNoAndBatchId: function(archNo, batchId){
let sql = "select count(*) as num from " + tabName +
this.where({archNo: archNo, batchId: batchId});
return sqlite.selectBySQL(sql);
},
where: function(queryParam, alias){
let where = ''
if(strUtil.isEmpty(alias)){
alias = ''
} else {
alias +='.'
}
if(queryParam){
var batchId = queryParam.batchId;
if(strUtil.isNotEmpty(batchId)){
where += ` and ${alias}batchId = ${batchId} `;
}
var boxId = queryParam.boxId;
if(strUtil.isNotEmpty(boxId)){
where += ` and ${alias}boxId = ${boxId}`;
}
var archNo = queryParam.archNo;
if(strUtil.isNotEmpty(archNo)){
where += ` and ${alias}archNo = "${archNo}" `
}
var searchValue = queryParam.searchValue;
if(strUtil.isNotEmpty(searchValue)){
where += ` and ( ${alias}boxNo = "${searchValue}" or ${alias}storeroom = "${searchValue}" or ${alias}archNo like "%${searchValue}%" )`
}
}
if(where.length > 0){
where = " where 1=1 " + where;
}
return where;
},
limit: function(queryParam){
let num = queryParam.pageNum;
let size = queryParam.pageSize
let numindex = 0
if(num <= 1){
numindex = 0
} else {
numindex = ((num - 1) * size)
}
return ` limit ${numindex},${size}`
}
}
3、初始化文件index.js
// 定义数据库实例名称
let dbName = "transfer"
export const openDb = () => {
//如果数据库存在则打开,不存在则创建。
return new Promise((resolve, reject) => {
plus.sqlite.openDatabase({
name: dbName, //数据库名称
// path: `_doc/${dbName}.db`, //数据库地址
path: `/storage/emulated/0/transfer/${dbName}.db`,
success(e) {
console.log(e)
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 查询所有数据表名
export const getTable = () => {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: "select * FROM sqlite_master where type='table'",
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 查询表数据总条数
export const getCount = (tabName) => {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: "select count(*) as num from " + tabName,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
}
// 查询表是否存在
export const isTable = (tabName) => {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: `select count(*) as isTable FROM sqlite_master where type='table' and name='${tabName}'`,
success(e) {
resolve(e[0].isTable ? true : false);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 修改数据
export const updateBySQL = (sql) => {
console.log(sql)
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: sql,
success(e) {
console.log(e)
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 修改数据
export const updateSQL = (tabName, setData, setName, setVal) => {
if (JSON.stringify(setData) !== '{}') {
let dataKeys = Object.keys(setData)
let setStr = ''
dataKeys.forEach((item, index) => {
console.log(setData[item])
setStr += (
`${item} = ${JSON.stringify(setData[item])}${dataKeys.length - 1 !== index ? "," : ""}`)
})
console.log(setStr)
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: `update ${tabName} set ${setStr} where ${setName} = "${setVal}"`,
success(e) {
console.log(e)
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
} else {
return new Promise((resolve, reject) => {
reject("错误")
});
}
}
//删除数据库数据
export const deleteInformationType = (tabName,setData) => {
if (JSON.stringify(setData) !== '{}') {
let dataKeys = Object.keys(setData)
let setStr = ''
dataKeys.forEach((item, index) => {
console.log(setData[item])
setStr += (
`${item}=${JSON.stringify(setData[item])}${dataKeys.length - 1 !== index ? " and " : ""}`)
})
let sql = `delete from ${tabName} where ${setStr}`
console.log(sql)
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
} else {
return new Promise((resolve, reject) => {
reject("错误")
});
}
}
//关闭数据库
export const closeSQL = () => {
return new Promise((resolve, reject) => {
plus.sqlite.closeDatabase({
name: dbName,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
}
//监听数据库是否开启
export const isOpen = () => {
let open = plus.sqlite.isOpenDatabase({
name: dbName,
path: `_doc/${dbName}.db`,
})
return open;
}
// 创建表
export const createTab = (tabName, data) => {
// tabName不能用数字作为表格名的开头
return new Promise((resolve, reject) => {
console.log("创建数据库表",tabName)
plus.sqlite.executeSql({
name: dbName,
// sql: 'create table if not exists dataList("list" INTEGER PRIMARY KEY AUTOINCREMENT,"id" TEXT,"name" TEXT,"gender" TEXT,"avatar" TEXT)',
sql: `create table if not exists ${tabName}(${data})`,
success(e) {
cosole.log("创建数据表成功")
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 创建表
export const dropTab = (tabName) => {
if(null == tabName || tabName.length == 0){
return false;
}
// tabName不能用数字作为表格名的开头
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
// sql: 'create table if not exists dataList("list" INTEGER PRIMARY KEY AUTOINCREMENT,"id" TEXT,"name" TEXT,"gender" TEXT,"avatar" TEXT)',
sql: `DROP TABLE ${tabName}`,
success(e) {
cosole.log(`删除数据表成功`)
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 添加数据
export const addTabItem = (tabName,obj) => {
if (obj) {
let keys = Object.keys(obj)
let keyStr = keys.toString()
let valStr = ''
keys.forEach((item, index) => {
if (keys.length - 1 == index) {
valStr += ('"' + obj[item] + '"')
} else {
valStr += ('"' + obj[item] + '",')
}
})
console.log(valStr)
let sqlStr = `insert into ${tabName}(${keyStr}) values(${valStr})`
console.log(sqlStr)
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: sqlStr,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
} else {
return new Promise((resolve, reject) => {
reject("错误")
})
}
}
// 合并数据
export const mergeSql = (tabName,tabs) => {
if (!tabs || tabs.length == 0) {
return new Promise((resolve, reject) => {
reject("错误")
})
}
let itemValStr = ''
tabs.forEach((item, index) => {
let itemKey = Object.keys(item)
let itemVal = ''
itemKey.forEach((key, i) => {
if (itemKey.length - 1 == i) {
if (typeof item[key] == 'object') {
itemVal += (`'${JSON.stringify(item[key])}'`)
} else {
itemVal += (`'${item[key]}'`)
}
} else {
if (typeof item[key] == 'object') {
itemVal += (`'${JSON.stringify(item[key])}',`)
} else {
itemVal += (`'${item[key]}',`)
}
}
})
if (tabs.length - 1 == index) {
itemValStr += ('(' + itemVal + ')')
} else {
itemValStr += ('(' + itemVal + '),')
}
})
let keys = Object.keys(tabs[0])
let keyStr = keys.toString()
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: `insert or ignore into ${tabName} (${keyStr}) values ${itemValStr}`,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
// 获取分页数据库数据
export const getDataList = async (tabName, num, size,byName,byType) => {
let count = 0
let sql = ''
let numindex = 0
await getCount(tabName).then((resNum) => {
count = Math.ceil(resNum[0].num / size)
})
if(((num - 1) * size) == 0) {
numindex = 0
} else {
numindex = ((num - 1) * size)
}
sql = `select * from ${tabName}`
if(byName && byType) {
// desc asc
sql += ` order by ${byName} ${byType}`
}
sql += ` limit ${numindex},${size}`
console.log(sql)
if (count < num - 1) {
return new Promise((resolve, reject) => {
reject("无数据")
});
} else {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
// sql: "select * from userInfo limit 3 offset 3",
sql:sql ,
success(e) {
resolve(e);
},
fail(e) {
reject(e);
}
})
})
}
}
//查询数据库数据
export const selectDataList = (tabName,setData,byName,byType) => {
let setStr = ''
let sql = ''
if (JSON.stringify(setData) !== '{}') {
let dataKeys = Object.keys(setData)
dataKeys.forEach((item, index) => {
console.log(setData[item])
setStr += (
`${item}=${JSON.stringify(setData[item])}${dataKeys.length - 1 !== index ? " and " : ""}`)
})
sql = `select * from ${tabName} where ${setStr}`
} else {
sql = `select * from ${tabName}`
}
if(byName && byType) {
// desc asc
sql += ` order by ${byName} ${byType}`
}
console.log(sql)
if (tabName !== undefined) {
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
} else {
return new Promise((resolve, reject) => {
reject("错误")
});
}
}
//查询数据库数据
export const selectBySQL = (sql) => {
console.log(sql)
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: sql,
success(e) {
console.log('success')
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
})
}
/**
* 创建一般索引
* @param tableName 表名
* @param indexName 索引名
* @param fieldName 字段名
*/
export const createIndex = (tableName, indexName, fieldName) => {
let sql = `CREATE INDEX ${indexName} ON ${tableName} (${fieldName})`
console.log('新建索引', sql)
return new Promise((resolve, reject) => {
plus.sqlite.executeSql({
name: dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
});
}
/**
* 判断表索引是否存在
* @param tableName 表名
* @param indexName 索引名
*/
export const existsIndex = (tableName, indexName) => {
let sql = `SELECT count(*) num FROM sqlite_master WHERE type = 'index' and name = '${indexName}' and tbl_name = '${tableName}'`
console.log(tableName, indexName, sql)
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
});
}
/**
* 判断表字段是否存在
* @param tableName 表名
* @param fieldName 字段名
*/
export const existsField = (tableName, fieldName) => {
let sql = `SELECT count(*) num FROM sqlite_master WHERE type = 'table' and name = '${fieldName}' and tbl_name = '${tableName}'`
console.log(tableName, fieldName, sql)
return new Promise((resolve, reject) => {
plus.sqlite.selectSql({
name: dbName,
sql: sql,
success(e) {
resolve(e);
},
fail(e) {
console.log(e)
reject(e);
}
})
});
}
//把这些方法导出去
export const sqlite = {
isOpen,
openDb,
createTab,dropTab,
mergeSql,
getDataList,
addTabItem,
closeSQL,
deleteInformationType,
getTable,
getCount,
updateSQL,
isTable,
selectDataList,
selectBySQL,updateBySQL,
createIndex,
existsIndex,
existsField,
};
4、打开数据库
在App.vue 文件的onLaunch: function() {} 函数中
sqlite 需要依赖存储权限,因此需要在成功获取权限后,初始化相关操作。
// 判断有没有存储权限
var _this = this
plus.android.requestPermissions(['android.permission.WRITE_EXTERNAL_STORAGE'], function(e) {
console.log(e.deniedPresent)
if (e.deniedAlways.length > 0) { //权限被永久拒绝
// 弹出提示框解释为何需要读写手机储存权限,引导用户打开设置页面开启
uni.showModal({
title: '存储权限',
content: '您拒绝了存储权限,请去设置-应用开启存储权限。',
success: function (res) {
if (res.confirm) {
// console.log('用户点击确定');
} else if (res.cancel) {
// console.log('用户点击取消');
}
}
});
}
if (e.deniedPresent.length > 0) { //权限被临时拒绝
// 弹出提示框解释为何需要读写手机储存权限,可再次调用plus.android.requestPermissions申请权限
plus.android.requestPermissions(['android.permission.WRITE_EXTERNAL_STORAGE'])
// console.log('666666666 ' + e.deniedPresent.toString());
}
if (e.granted.length > 0) { //权限被允许
//调用依赖获取读写手机储存权限的代码
// _this.upload() // 获取权限成功之后调用的函数
// console.log('2222222222 ' + e.granted.toString());
if(!sqlite.isOpen()){
sqlite.openDb().then((res) => {
SysBoxDetail.init();
// ......
});
}
}
}, function(e) {
// console.log('R12133313221' + JSON.stringify(e));
});
5、查询数据
在sqlite表的脚本文件中,已将所有函数export
在vue中,先引入相关文件,后续可以直接使用
import {SysBoxDetail} from '@/db/sqlite/boxDetail.js'
SysBoxDetail.selectAll(this.queryParams).then((res) => {
console.log(res)
})
6、可视化测试
<template>
<view class="content">
<view class="">
表名
</view>
<radio-group @change="radioChange">
<label class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in tableList" :key="index">
<view>
<radio :value="index" :checked="index == radioIndex" />{{item.name}}
</view>
</label>
</radio-group>
<button type="primary" @click="dbType">检查数据库打开状态</button>
<button type="primary" @click="openDb">打开数据库</button>
<button type="primary" @click="closeDb">关闭数据库</button>
<input class="input_box" type="text" value="" v-model="valInp" placeholder="请输入聊天内容" />
<button type="primary" @click="addSql">添加数据</button>
<button type="primary" @click="setSql">修改数据</button>
<input class="input_box" type="text" value="" v-model="valTab" placeholder="请输入表名" />
<button type="primary" @click="addExecuteSql">添加表</button>
<button type="primary" @click="tapMerge">合并数据</button>
<button type="primary" @click="delSql">删除数据库数据</button>
<button type="primary" @click="getTab">查询所有数据表名</button>
<button type="primary" @click="tapIsTab">查询表是否存在</button>
<button type="primary" @click="getNum">获取表数据总条数</button>
<button type="primary" @click="getSql">获取数据库数据</button>
<button type="primary" @click="getBranch">获取分页数据库数据+排序</button>
<t-table class="table_box" :chatTableListLength="chatTableListLength" :chatTableList="chatTableList">
</t-table>
</view>
</template>
<script>
import {
*
} from '@/db/sqlite/index.js'
export default {
components: {
tTable
},
data() {
return {
valTab:'',
radioIndex:0,
valInp: '',
chatTableList: [],
tableList:[],
chatTableListLength: 0,
tableName:''
}
},
async onLoad() {
await this.openDb()
await this.getTab()
},
methods: {
radioChange(val) {
this.tableName = this.tableList[val.detail.value].name
console.log(this.tableList)
},
tapMerge() {
let tabs = [{
"local_id": "3_wtsu83kox2",
"id": "3_wtsu83koxx",
"chat_friend_id": "兔兔",
"content": 2
}, {
"local_id": "3_hq5uyx9vrd",
"id": "3_hq5uyx9vrd",
"chat_friend_id": "兔兔",
"content": 2
}]
// 只会插入不重复的
mergeSql('pop', this.tableName, tabs).then((res) => {
console.log(res)
})
},
tapIsTab() {
isTable('pop', this.tableName).then((res) => {
console.log(res)
})
},
getTab() {
getTable('pop').then((res) => {
console.log(res)
this.tableList = res
})
},
addExecuteSql() {
console.log(this.valTab)
if(!this.valTab || this.valTab == '') return
addTab('pop', this.valTab).then(res => {
console.log(res)
this.valTab = ''
this.getTab()
})
},
delSql() {
deleteInformationType('pop', this.tableName, {
"chat_i": 2,
}).then(res => {
console.log(res)
})
},
dbType() {
uni.showToast({
icon: "none",
title: isOpen('pop') ? '数据库已打开' : '数据库已关闭'
})
},
closeDb() {
closeSQL().then(res => {
console.log(res)
})
},
openDb() {
if (isOpen('pop')) return
openDb('pop').then(res => {
console.log(res)
})
},
setSql() {
updateSQL('pop', this.tableName, {
chat_friend_id: 'Texas3333666',
content: '3333666'
}, 'local_id', '3_bpu8sufahoe').then(res => {
console.log('1111')
})
},
addSql() {
console.log(this.valInp)
console.log(this.tableName)
let _this = this
if (this.valInp == '') {
uni.showToast({
icon: "none",
title: "请输入聊天内容"
})
return
}
let objMsg = {}
let local_id = 3 + '_' + this.getLocalId()
let content = {}
content.voiceTime = 0
content.url = ''
content.tempFilePath = ''
content.text = this.valInp
objMsg.id = local_id
objMsg.chat_friend_id = 3
objMsg.user_id = 1
objMsg.local_id = local_id
objMsg.is_read = 0
objMsg.status = 0
objMsg.send_time = parseInt(new Date().getTime() / 1000)
objMsg.msg_type = 1
objMsg.content = JSON.stringify(content)
addTabItem('pop', _this.tableName, {
id: objMsg.id,
local_id: objMsg.id,
content: '2',
chat_friend_id: _this.valInp,
}).then((res) => {
_this.valInp = ''
console.log(_this.valInp)
})
},
// 获取本地id
getLocalId() {
return Math.random().toString(36).slice(2)
},
getSql() {
selectDataList('pop', this.tableName,{}).then(res => {
console.log(res)
this.chatTableList = res
})
},
getBranch() {
getDataList('pop', this.tableName, 1, 20, 'chat_i', 'desc').then(res => {
console.log(res)
this.chatTableList = res
}).catch(err => {
console.log(err)
})
},
getNum() {
getCount('pop', this.tableName).then(res => {
this.chatTableListLength = res[0].num
})
}
}
}
</script>
<style>
.content button {
margin-bottom: 20rpx;
}
.input_box {
width: 690rpx;
height: 80rpx;
font-size: 40rpx;
line-height: 40rpx;
margin: 10rpx auto;
background-color: pink;
}
.table_box {
width: 690rpx;
margin: 10rpx auto;
text-align: center;
}
</style>
参照文章
SQLite 教程 | 菜鸟教程 (runoob.com)