From caea0b4b99fd854b9d295e183f7c844eb7c49d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Fri, 28 Mar 2025 16:20:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E5=A4=87=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E8=BF=87=E6=BB=A4=E7=AD=9B=E9=80=89=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/api/device.js | 84 ++ Cunkebao/pages/device/index.vue | 832 ++++++++++++++---- Cunkebao/utils/request.js | 52 +- .../application/devices/controller/Device.php | 22 +- Server/application/devices/model/Device.php | 32 +- 5 files changed, 793 insertions(+), 229 deletions(-) create mode 100644 Cunkebao/api/device.js diff --git a/Cunkebao/api/device.js b/Cunkebao/api/device.js new file mode 100644 index 00000000..d233516c --- /dev/null +++ b/Cunkebao/api/device.js @@ -0,0 +1,84 @@ +import request from '@/utils/request' + +/** + * 获取设备列表 + * @param {Object} params 查询参数 + * @param {string} params.keyword 关键词搜索(同时搜索IMEI和备注) + * @param {number} params.alive 设备在线状态(可选,1:在线 0:离线) + * @param {number} params.page 页码 + * @param {number} params.limit 每页数量 + * @returns {Promise} 设备列表 + * + * 注意: params 参数会被自动添加到URL查询字符串中,如 /v1/devices?keyword=xxx&alive=1&page=1&limit=20 + */ +export function getDeviceList(params) { + return request({ + url: '/v1/devices', + method: 'GET', + params + }) +} + +/** + * 获取设备总数 + * @param {Object} params 查询参数 + * @param {number} params.alive 设备在线状态(可选,1:在线 0:离线) + * @returns {Promise} 设备总数 + */ +export function getDeviceCount(params) { + return request({ + url: '/v1/devices/count', + method: 'GET', + params + }) +} + +/** + * 获取设备详情 + * @param {number} id 设备ID + * @returns {Promise} 设备详情 + */ +export function getDeviceDetail(id) { + return request({ + url: `/v1/devices/${id}`, + method: 'GET' + }) +} + +/** + * 删除设备 + * @param {number} id 设备ID + * @returns {Promise} 删除结果 + */ +export function deleteDevice(id) { + return request({ + url: `/v1/devices/${id}`, + method: 'DELETE' + }) +} + +/** + * 刷新设备状态 + * @returns {Promise} 刷新结果 + */ +export function refreshDevices() { + return request({ + url: '/v1/devices/refresh', + method: 'PUT' + }) +} + +/** + * 添加设备 + * @param {Object} data 设备数据 + * @param {string} data.imei 设备IMEI + * @param {string} data.memo 设备备注 + * @returns {Promise} 添加结果 + */ +export function addDevice(data) { + return request({ + url: '/v1/devices', + method: 'POST', + data + }) +} \ No newline at end of file diff --git a/Cunkebao/pages/device/index.vue b/Cunkebao/pages/device/index.vue index 2addc163..0d6ebcad 100644 --- a/Cunkebao/pages/device/index.vue +++ b/Cunkebao/pages/device/index.vue @@ -19,11 +19,11 @@ 总设备数 - 42 + {{ totalDeviceCount }} 在线设备 - 35 + {{ onlineDeviceCount }} @@ -40,6 +40,9 @@ bgColor="#fff" searchIconSize="50" shape="round" + @search="handleSearch" + @clear="handleClearSearch" + @input="handleSearchInput" > @@ -56,8 +59,20 @@ {{ statusText }} - - + + + {{ searchKeyword }} + × + + + 全选 @@ -67,83 +82,66 @@ - - - - - - - - 设备 1 - 在线 - - IMEI: sd123123 - 微信号: wxid_hxdxdoal - - 好友数: 435 - 今日新增: +20 - - + + + + 加载中... - - - - - - - - 设备 2 - 在线 - - IMEI: sd123124 - 微信号: wxid_2i7sncgq - - 好友数: 143 - 今日新增: +26 - - + + + - - - - - - - - 设备 3 - 在线 + + @@ -190,13 +188,37 @@ + + + + + + IMEI详情 + + × + + + + + IMEI: + {{ currentImei }} + + + 复制IMEI + + + + + - + @@ -518,8 +803,8 @@ export default { color: #333; padding: 10rpx 20rpx; border-radius: 20rpx; - background-color: #fff; - border: 1rpx solid #e5e5e5; + background-color: #f0f5ff; + border: 1rpx solid #d6e4ff; min-width: 180rpx; justify-content: space-between; @@ -528,7 +813,35 @@ export default { } &:active { - background-color: #f8f8f8; + background-color: #e0ebff; + } + } + + .filter-tag { + display: inline-flex; + align-items: center; + font-size: 24rpx; + color: #4080ff; + padding: 6rpx 16rpx; + border-radius: 16rpx; + background-color: #f0f5ff; + border: 1rpx solid #d6e4ff; + margin-left: 16rpx; + margin-right: auto; + max-width: 250rpx; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + .close-icon { + margin-left: 12rpx; + font-size: 28rpx; + line-height: 24rpx; + color: #999; + + &:active { + color: #666; + } } } @@ -618,6 +931,20 @@ export default { color: #666; } + .device-imei { + cursor: pointer; + + .imei-text { + color: #4080ff; + padding-left: 10rpx; + font-size: 32rpx; + } + + &:active { + opacity: 0.7; + } + } + .device-likes { display: flex; justify-content: space-between; @@ -626,6 +953,33 @@ export default { } } } + + .loading-container, .empty-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 100rpx 0; + + .loading-text { + font-size: 28rpx; + color: #999; + margin-top: 20rpx; + } + } + + .load-more { + display: flex; + align-items: center; + justify-content: center; + padding: 30rpx 0; + + .load-more-text { + font-size: 28rpx; + color: #999; + margin-left: 10rpx; + } + } } /* 添加设备弹窗样式 */ @@ -740,4 +1094,104 @@ export default { } } } + +/* IMEI详情模态框样式 */ +.imei-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + + .modal-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + } + + .modal-content { + position: relative; + width: 600rpx; + background-color: #fff; + border-radius: 20rpx; + overflow: hidden; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 30rpx 40rpx; + + .modal-title { + font-size: 36rpx; + font-weight: bold; + color: #333; + } + + .modal-close { + padding: 10rpx; + + .close-icon { + font-size: 44rpx; + color: #777; + line-height: 1; + } + } + } + + .modal-body { + padding: 20rpx 40rpx 40rpx; + + .imei-detail { + background-color: #f5f7fa; + padding: 30rpx; + border-radius: 10rpx; + margin-bottom: 30rpx; + + .imei-label { + display: block; + font-size: 28rpx; + color: #666; + margin-bottom: 10rpx; + } + + .imei-value { + display: block; + font-size: 32rpx; + color: #333; + word-break: break-all; + font-family: monospace; + line-height: 1.5; + } + } + + .copy-btn { + display: flex; + align-items: center; + justify-content: center; + background-color: #4080ff; + color: #fff; + padding: 20rpx 0; + border-radius: 10rpx; + + .copy-text { + margin-left: 10rpx; + font-size: 28rpx; + } + + &:active { + opacity: 0.8; + } + } + } + } +} \ No newline at end of file diff --git a/Cunkebao/utils/request.js b/Cunkebao/utils/request.js index 8868477c..47966488 100644 --- a/Cunkebao/utils/request.js +++ b/Cunkebao/utils/request.js @@ -91,6 +91,25 @@ function responseInterceptor(response) { return response.data; } +/** + * 构建完整的URL,包括查询参数 + * @param {string} baseUrl 基础URL + * @param {Object} params 查询参数 + * @returns {string} 完整的URL + */ +function buildUrlWithParams(baseUrl, params) { + if (!params || Object.keys(params).length === 0) { + return baseUrl; + } + + const queryString = Object.keys(params) + .filter(key => params[key] !== undefined && params[key] !== null) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) + .join('&'); + + return queryString ? `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${queryString}` : baseUrl; +} + /** * 统一请求函数 * @param {Object} options 请求选项 @@ -110,12 +129,24 @@ function request(options) { // 请求拦截 const interceptedConfig = requestInterceptor(config); + // 处理GET请求参数 + let url = `${interceptedConfig.baseURL}${interceptedConfig.url}`; + const method = interceptedConfig.method || 'GET'; + + // 如果是GET请求并且有params参数,将其转换为URL查询字符串 + if (method.toUpperCase() === 'GET' && interceptedConfig.params) { + url = buildUrlWithParams(url, interceptedConfig.params); + + // 打印完整请求URL(便于调试) + console.log('完整请求URL:', url); + } + // 发起请求 return new Promise((resolve, reject) => { uni.request({ - url: `${interceptedConfig.baseURL}${interceptedConfig.url}`, - method: interceptedConfig.method || 'GET', - data: interceptedConfig.data, + url: url, + method: method, + data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data, header: interceptedConfig.header, timeout: interceptedConfig.timeout, success: (res) => { @@ -127,12 +158,25 @@ function request(options) { } }, fail: (err) => { + // 显示提示 uni.showToast({ title: '网络请求失败', icon: 'none', duration: 2000 }); - reject(err); + + // 增强错误对象,添加更多信息便于调试 + const enhancedError = { + ...err, + url: url, + method: method, + params: method.toUpperCase() === 'GET' ? interceptedConfig.params : undefined, + data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data, + message: err.errMsg || '网络请求失败' + }; + + console.error('请求失败详情:', enhancedError); + reject(enhancedError); } }); }); diff --git a/Server/application/devices/controller/Device.php b/Server/application/devices/controller/Device.php index a95b653e..9c264a3f 100644 --- a/Server/application/devices/controller/Device.php +++ b/Server/application/devices/controller/Device.php @@ -106,26 +106,14 @@ class Device extends Controller try { // 获取登录用户信息 $userInfo = request()->userInfo; - if (empty($userInfo)) { - return json([ - 'code' => 401, - 'msg' => '未登录或登录已过期' - ]); - } - // 获取查询条件 $where = []; - // 设备IMEI - $imei = Request::param('imei'); - if (!empty($imei)) { - $where['d.imei'] = ['like', "%{$imei}%"]; - } - - // 设备备注 - $memo = Request::param('memo'); - if (!empty($memo)) { - $where['d.memo'] = ['like', "%{$memo}%"]; + // 关键词搜索(同时搜索IMEI和备注) + $keyword = Request::param('keyword'); + if (!empty($keyword)) { + // 使用复杂条件实现OR查询 + $where[] = ['exp', "d.imei LIKE '%{$keyword}%' OR d.memo LIKE '%{$keyword}%'"]; } // 设备在线状态 diff --git a/Server/application/devices/model/Device.php b/Server/application/devices/model/Device.php index cff3748e..507b9139 100644 --- a/Server/application/devices/model/Device.php +++ b/Server/application/devices/model/Device.php @@ -79,31 +79,25 @@ class Device extends Model $where['d.isDeleted'] = 0; } - // 处理查询条件,避免排序规则冲突 - $conditions = []; - foreach ($where as $key => $value) { - // 对于涉及 JOIN 的字段特殊处理 - if (strpos($key, 'imei') !== false) { - // 删除原本的 imei 条件,避免直接使用它 - continue; - } - $conditions[$key] = $value; - } - + // 构建查询对象 $query = self::alias('d') ->field(['d.id', 'd.imei', 'd.memo', 'w.wechatId', 'd.alive', 'w.totalFriend']) - ->leftJoin('tk_wechat_account w', 'd.imei = w.imei COLLATE utf8mb4_unicode_ci') - ->where($conditions); + ->leftJoin('tk_wechat_account w', 'd.imei = w.imei COLLATE utf8mb4_unicode_ci'); - // 单独处理 imei 搜索条件,确保使用相同的排序规则 - if (isset($where['imei'])) { - if (is_array($where['imei']) && isset($where['imei'][0]) && $where['imei'][0] === 'like') { - $query->where('d.imei', 'like', $where['imei'][1]); - } else { - $query->where('d.imei', $where['imei']); + // 处理查询条件 + foreach ($where as $key => $value) { + // 处理特殊的exp表达式条件 + if (is_numeric($key) && is_array($value) && isset($value[0]) && $value[0] === 'exp') { + // 直接添加原始SQL表达式 + $query->whereExp('', $value[1]); + continue; } + + // 处理普通条件 + $query->where($key, $value); } + // 返回分页结果 return $query->order($order) ->paginate($limit, false, ['page' => $page]); }