新增功能
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# 腾讯云实时音视频(TRTC)配置
|
||||
# 在腾讯云控制台获取:https://console.cloud.tencent.com/trtc
|
||||
|
||||
# SDK AppID
|
||||
TRTC_SDK_APP_ID=
|
||||
|
||||
# 密钥
|
||||
TRTC_SECRET_KEY=
|
||||
|
||||
# 是否启用
|
||||
TRTC_ENABLE=false
|
||||
@@ -0,0 +1,379 @@
|
||||
# 医生预约系统后端安装说明
|
||||
|
||||
## 文件清单
|
||||
|
||||
已创建以下后端文件:
|
||||
|
||||
### 1. 数据库模型
|
||||
- `server/app/common/model/doctor/Appointment.php` - 预约模型
|
||||
|
||||
### 2. 控制器
|
||||
- `server/app/adminapi/controller/doctor/AppointmentController.php` - 预约控制器
|
||||
|
||||
### 3. 业务逻辑层
|
||||
- `server/app/adminapi/logic/doctor/AppointmentLogic.php` - 预约业务逻辑
|
||||
|
||||
### 4. 验证器
|
||||
- `server/app/adminapi/validate/doctor/AppointmentValidate.php` - 预约数据验证
|
||||
|
||||
### 5. 数据列表
|
||||
- `server/app/adminapi/lists/doctor/AppointmentLists.php` - 预约列表
|
||||
|
||||
### 6. 数据库脚本
|
||||
- `server/sql/doctor_appointment.sql` - 数据库表创建脚本
|
||||
|
||||
## 安装步骤
|
||||
|
||||
### 1. 创建数据库表
|
||||
|
||||
在MySQL中执行以下SQL脚本:
|
||||
|
||||
```bash
|
||||
mysql -u your_username -p your_database < server/sql/doctor_appointment.sql
|
||||
```
|
||||
|
||||
或者在phpMyAdmin中导入 `server/sql/doctor_appointment.sql` 文件。
|
||||
|
||||
### 2. 验证文件
|
||||
|
||||
确认所有文件已正确创建:
|
||||
|
||||
```bash
|
||||
# 检查模型
|
||||
ls -la server/app/common/model/doctor/Appointment.php
|
||||
|
||||
# 检查控制器
|
||||
ls -la server/app/adminapi/controller/doctor/AppointmentController.php
|
||||
|
||||
# 检查逻辑层
|
||||
ls -la server/app/adminapi/logic/doctor/AppointmentLogic.php
|
||||
|
||||
# 检查验证器
|
||||
ls -la server/app/adminapi/validate/doctor/AppointmentValidate.php
|
||||
|
||||
# 检查列表
|
||||
ls -la server/app/adminapi/lists/doctor/AppointmentLists.php
|
||||
```
|
||||
|
||||
### 3. 清除缓存
|
||||
|
||||
```bash
|
||||
cd server
|
||||
php think clear
|
||||
```
|
||||
|
||||
### 4. 测试API接口
|
||||
|
||||
#### 4.1 获取可用时间段
|
||||
|
||||
```bash
|
||||
GET /adminapi/doctor.appointment/availableSlots?doctor_id=1&appointment_date=2026-03-04&period=morning
|
||||
```
|
||||
|
||||
预期返回:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"slots": [
|
||||
{
|
||||
"time": "09:00",
|
||||
"available": true,
|
||||
"quota": 5
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 创建预约
|
||||
|
||||
```bash
|
||||
POST /adminapi/doctor.appointment/create
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"patient_id": 1,
|
||||
"doctor_id": 1,
|
||||
"appointment_date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"appointment_time": "09:00",
|
||||
"appointment_type": "video",
|
||||
"remark": "测试预约"
|
||||
}
|
||||
```
|
||||
|
||||
预期返回:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "预约成功",
|
||||
"data": {
|
||||
"id": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 获取预约列表
|
||||
|
||||
```bash
|
||||
GET /adminapi/doctor.appointment/lists?page_no=1&page_size=10
|
||||
```
|
||||
|
||||
#### 4.4 取消预约
|
||||
|
||||
```bash
|
||||
POST /adminapi/doctor.appointment/cancel
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.5 获取医生可用号源数
|
||||
|
||||
```bash
|
||||
GET /adminapi/doctor.appointment/doctorAvailability?doctor_id=1&date=2026-03-04
|
||||
```
|
||||
|
||||
预期返回:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"available_count": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API接口说明
|
||||
|
||||
### 1. 获取可用时间段
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/availableSlots`
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| doctor_id | int | 是 | 医生ID |
|
||||
| appointment_date | date | 是 | 预约日期 YYYY-MM-DD |
|
||||
| period | string | 是 | 时段 morning/afternoon |
|
||||
|
||||
**返回数据**:
|
||||
```json
|
||||
{
|
||||
"slots": [
|
||||
{
|
||||
"time": "09:00",
|
||||
"available": true,
|
||||
"quota": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 创建预约
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/create`
|
||||
|
||||
**请求方式**: POST
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| patient_id | int | 是 | 患者ID |
|
||||
| doctor_id | int | 是 | 医生ID |
|
||||
| appointment_date | date | 是 | 预约日期 |
|
||||
| period | string | 是 | 时段 morning/afternoon |
|
||||
| appointment_time | time | 是 | 预约时间 HH:mm |
|
||||
| appointment_type | string | 否 | 预约类型 video/text/phone |
|
||||
| remark | string | 否 | 备注 |
|
||||
|
||||
**返回数据**:
|
||||
```json
|
||||
{
|
||||
"id": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 取消预约
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/cancel`
|
||||
|
||||
**请求方式**: POST
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | int | 是 | 预约ID |
|
||||
|
||||
### 4. 预约列表
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/lists`
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| page_no | int | 否 | 页码 |
|
||||
| page_size | int | 否 | 每页数量 |
|
||||
| patient_id | int | 否 | 患者ID |
|
||||
| doctor_id | int | 否 | 医生ID |
|
||||
| status | int | 否 | 状态 |
|
||||
| appointment_date | date | 否 | 预约日期 |
|
||||
|
||||
### 5. 预约详情
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/detail`
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | int | 是 | 预约ID |
|
||||
|
||||
### 6. 医生可用号源数
|
||||
|
||||
**接口地址**: `/adminapi/doctor.appointment/doctorAvailability`
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| doctor_id | int | 是 | 医生ID |
|
||||
| date | date | 是 | 日期 YYYY-MM-DD |
|
||||
|
||||
## 业务逻辑说明
|
||||
|
||||
### 1. 时间段生成规则
|
||||
|
||||
- 上午时段:09:00 - 11:30(30分钟间隔)
|
||||
- 下午时段:13:00 - 20:30(30分钟间隔)
|
||||
|
||||
### 2. 预约规则
|
||||
|
||||
1. 必须有医生排班且状态为"出诊"(status=1)
|
||||
2. 时间段不能重复预约
|
||||
3. 预约数量不能超过排班设置的号源数(quota)
|
||||
4. 使用数据库唯一索引防止并发冲突
|
||||
|
||||
### 3. 状态说明
|
||||
|
||||
**预约状态**:
|
||||
- 1: 已预约
|
||||
- 2: 已取消
|
||||
- 3: 已完成
|
||||
|
||||
**排班状态**:
|
||||
- 1: 出诊
|
||||
- 2: 停诊
|
||||
- 3: 休息
|
||||
- 4: 请假
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **并发控制**: 使用数据库唯一索引 `unique_appointment` 防止重复预约
|
||||
2. **事务处理**: 创建预约时使用事务确保数据一致性
|
||||
3. **时间格式**:
|
||||
- 日期格式:YYYY-MM-DD
|
||||
- 时间格式:HH:mm
|
||||
- 时间戳:整型(秒)
|
||||
4. **权限控制**: 需要在后台权限管理中添加相应的权限节点
|
||||
|
||||
## 权限节点
|
||||
|
||||
建议添加以下权限节点:
|
||||
|
||||
```
|
||||
doctor.appointment/availableSlots - 查看可用时间段
|
||||
doctor.appointment/create - 创建预约
|
||||
doctor.appointment/cancel - 取消预约
|
||||
doctor.appointment/lists - 预约列表
|
||||
doctor.appointment/detail - 预约详情
|
||||
doctor.appointment/doctorAvailability - 医生可用号源
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 1. 控制器不存在
|
||||
|
||||
**错误**: `控制器不存在: app\adminapi\controller\doctor\AppointmentController`
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 清除缓存
|
||||
cd server
|
||||
php think clear
|
||||
|
||||
# 检查文件是否存在
|
||||
ls -la server/app/adminapi/controller/doctor/AppointmentController.php
|
||||
|
||||
# 检查命名空间是否正确
|
||||
head -n 5 server/app/adminapi/controller/doctor/AppointmentController.php
|
||||
```
|
||||
|
||||
### 2. 数据库表不存在
|
||||
|
||||
**错误**: `Table 'database.la_doctor_appointment' doesn't exist`
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 执行SQL脚本
|
||||
mysql -u username -p database < server/sql/doctor_appointment.sql
|
||||
|
||||
# 或在MySQL中手动执行
|
||||
mysql> source /path/to/server/sql/doctor_appointment.sql;
|
||||
```
|
||||
|
||||
### 3. 参数验证失败
|
||||
|
||||
**错误**: `参数错误`
|
||||
|
||||
**解决方案**:
|
||||
- 检查请求参数是否完整
|
||||
- 检查参数类型是否正确
|
||||
- 查看验证器规则是否匹配
|
||||
|
||||
## 测试数据
|
||||
|
||||
### 创建测试排班
|
||||
|
||||
```sql
|
||||
INSERT INTO `la_doctor_roster` (`doctor_id`, `date`, `period`, `status`, `quota`, `max_patients`, `create_time`, `update_time`)
|
||||
VALUES
|
||||
(1, '2026-03-04', 'morning', 1, 10, 15, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(1, '2026-03-04', 'afternoon', 1, 10, 15, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||||
```
|
||||
|
||||
### 创建测试预约
|
||||
|
||||
```sql
|
||||
INSERT INTO `la_doctor_appointment` (`patient_id`, `doctor_id`, `roster_id`, `appointment_date`, `period`, `appointment_time`, `appointment_type`, `status`, `create_time`, `update_time`)
|
||||
VALUES
|
||||
(1, 1, 1, '2026-03-04', 'morning', '09:00:00', 'video', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||||
```
|
||||
|
||||
## 完成状态
|
||||
|
||||
✅ 数据库模型创建完成
|
||||
✅ 控制器创建完成
|
||||
✅ 业务逻辑层创建完成
|
||||
✅ 验证器创建完成
|
||||
✅ 数据列表创建完成
|
||||
✅ SQL脚本创建完成
|
||||
⏳ 等待数据库表创建
|
||||
⏳ 等待接口测试
|
||||
|
||||
---
|
||||
|
||||
**创建时间**: 2024-02-26
|
||||
**最后更新**: 2024-02-26
|
||||
@@ -0,0 +1,229 @@
|
||||
# 批量排班接口修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
接口报错:
|
||||
```
|
||||
app\common\controller\BaseLikeAdminController::success():
|
||||
Argument #2 ($data) must be of type array, bool given
|
||||
```
|
||||
|
||||
## 问题原因
|
||||
|
||||
`RosterLogic::batchSave()` 方法返回 `true`(布尔值),但控制器的 `success()` 方法要求第二个参数必须是数组类型。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 修改 Logic 层返回值
|
||||
|
||||
**修改前**:
|
||||
```php
|
||||
public static function batchSave(array $params)
|
||||
{
|
||||
// ...
|
||||
Db::commit();
|
||||
return true; // 返回布尔值
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```php
|
||||
public static function batchSave(array $params)
|
||||
{
|
||||
// ...
|
||||
Db::commit();
|
||||
return [
|
||||
'success_count' => $successCount,
|
||||
'create_count' => $createCount,
|
||||
'update_count' => $updateCount,
|
||||
]; // 返回数组,包含统计信息
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修改控制器
|
||||
|
||||
**修改前**:
|
||||
```php
|
||||
public function batchSave()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('batchSave');
|
||||
$result = RosterLogic::batchSave($params);
|
||||
return $this->success('批量保存成功', $result); // $result 是 bool
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```php
|
||||
public function batchSave()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('batchSave');
|
||||
$result = RosterLogic::batchSave($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(RosterLogic::getError());
|
||||
}
|
||||
return $this->success('批量保存成功', $result); // $result 是 array
|
||||
}
|
||||
```
|
||||
|
||||
## 改进内容
|
||||
|
||||
### 1. 返回统计信息
|
||||
|
||||
现在批量保存接口会返回详细的统计信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "批量保存成功",
|
||||
"data": {
|
||||
"success_count": 20,
|
||||
"create_count": 15,
|
||||
"update_count": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `success_count`: 成功处理的记录数
|
||||
- `create_count`: 新创建的记录数
|
||||
- `update_count`: 更新的记录数
|
||||
|
||||
### 2. 错误处理
|
||||
|
||||
如果批量保存失败,会返回错误信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "错误信息",
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 同样修复了 copy 方法
|
||||
|
||||
`copy` 方法也有同样的问题,已一并修复。
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 测试1:批量创建
|
||||
|
||||
**请求**:
|
||||
```json
|
||||
POST /adminapi/doctor.roster/batchSave
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "afternoon",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "批量保存成功",
|
||||
"data": {
|
||||
"success_count": 2,
|
||||
"create_count": 2,
|
||||
"update_count": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 测试2:批量更新
|
||||
|
||||
再次执行相同的请求:
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "批量保存成功",
|
||||
"data": {
|
||||
"success_count": 2,
|
||||
"create_count": 0,
|
||||
"update_count": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
|
||||
修改的文件:
|
||||
1. `server/app/adminapi/logic/doctor/RosterLogic.php` - 修改返回值类型
|
||||
2. `server/app/adminapi/controller/doctor/RosterController.php` - 添加错误处理
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有返回布尔值的 Logic 方法都应该检查是否会传递给 `success()` 方法
|
||||
2. 如果需要返回数据,应该返回数组而不是布尔值
|
||||
3. 失败时应该使用 `fail()` 方法而不是 `success()`
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### Logic 层返回值规范
|
||||
|
||||
```php
|
||||
// ✅ 正确:返回数组或 false
|
||||
public static function someMethod($params)
|
||||
{
|
||||
try {
|
||||
// 处理逻辑
|
||||
return ['id' => $id, 'count' => $count];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 错误:返回 true
|
||||
public static function someMethod($params)
|
||||
{
|
||||
try {
|
||||
// 处理逻辑
|
||||
return true; // 不要这样做
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 控制器层处理规范
|
||||
|
||||
```php
|
||||
// ✅ 正确:检查返回值
|
||||
public function someAction()
|
||||
{
|
||||
$result = SomeLogic::someMethod($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(SomeLogic::getError());
|
||||
}
|
||||
return $this->success('操作成功', $result);
|
||||
}
|
||||
|
||||
// ❌ 错误:直接传递布尔值
|
||||
public function someAction()
|
||||
{
|
||||
$result = SomeLogic::someMethod($params);
|
||||
return $this->success('操作成功', $result); // 如果 $result 是 bool 会报错
|
||||
}
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
问题已修复,批量排班接口现在可以正常工作,并且返回详细的统计信息。
|
||||
@@ -0,0 +1,245 @@
|
||||
# 医生排班管理 - 后端实现说明
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
server/app/
|
||||
├── adminapi/
|
||||
│ ├── controller/doctor/
|
||||
│ │ └── RosterController.php # 排班控制器
|
||||
│ ├── lists/doctor/
|
||||
│ │ └── RosterLists.php # 排班列表
|
||||
│ ├── logic/doctor/
|
||||
│ │ └── RosterLogic.php # 排班逻辑
|
||||
│ └── validate/doctor/
|
||||
│ └── RosterValidate.php # 排班验证器
|
||||
└── common/
|
||||
└── model/doctor/
|
||||
└── Roster.php # 排班模型
|
||||
```
|
||||
|
||||
## 数据库表
|
||||
|
||||
表名:`la_doctor_roster`
|
||||
|
||||
```sql
|
||||
CREATE TABLE `la_doctor_roster` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`doctor_id` int(11) NOT NULL COMMENT '医生ID(对应 zyt_admin.id)',
|
||||
`date` date NOT NULL COMMENT '日期',
|
||||
`period` varchar(20) NOT NULL COMMENT '时段 morning/afternoon',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1-出诊 2-停诊 3-休息 4-请假',
|
||||
`quota` int(11) DEFAULT '0' COMMENT '号源数',
|
||||
`max_patients` int(11) DEFAULT '0' COMMENT '最大接诊数',
|
||||
`booked_count` int(11) DEFAULT '0' COMMENT '已预约数',
|
||||
`remark` varchar(500) DEFAULT '',
|
||||
`create_time` int(11) NOT NULL,
|
||||
`update_time` int(11) NOT NULL,
|
||||
`delete_time` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_doctor_date_period` (`doctor_id`,`date`,`period`,`delete_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 1. 获取排班列表
|
||||
|
||||
**URL**: `GET /adminapi/doctor.roster/lists`
|
||||
|
||||
**请求参数**:
|
||||
- `start_date`: 开始日期(必填)
|
||||
- `end_date`: 结束日期(必填)
|
||||
- `doctor_id`: 医生ID(可选)
|
||||
- `period`: 时段(可选,morning/afternoon)
|
||||
- `status`: 状态(可选,1/2/3/4)
|
||||
|
||||
**返回示例**:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"lists": [
|
||||
{
|
||||
"id": 1,
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"booked_count": 5,
|
||||
"remark": "",
|
||||
"create_time": 1709366400,
|
||||
"update_time": 1709366400
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"page_no": 1,
|
||||
"page_size": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 保存排班
|
||||
|
||||
**URL**: `POST /adminapi/doctor.roster/save`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": ""
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `id` 存在时为更新,不存在时为新增
|
||||
- `status`: 1-出诊 2-停诊 3-休息 4-请假
|
||||
- `period`: morning-上午 afternoon-下午
|
||||
|
||||
### 3. 删除排班
|
||||
|
||||
**URL**: `POST /adminapi/doctor.roster/delete`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"id": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 排班详情
|
||||
|
||||
**URL**: `GET /adminapi/doctor.roster/detail`
|
||||
|
||||
**请求参数**:
|
||||
- `id`: 排班ID
|
||||
|
||||
### 5. 批量保存排班
|
||||
|
||||
**URL**: `POST /adminapi/doctor.roster/batchSave`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "afternoon",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 复制排班
|
||||
|
||||
**URL**: `POST /adminapi/doctor.roster/copy`
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"source_start_date": "2026-03-02",
|
||||
"source_end_date": "2026-03-08",
|
||||
"target_start_date": "2026-03-09",
|
||||
"doctor_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 将源日期范围的排班复制到目标日期
|
||||
- `doctor_id` 可选,不传则复制所有医生的排班
|
||||
|
||||
## 核心逻辑
|
||||
|
||||
### RosterLogic::save()
|
||||
|
||||
保存排班逻辑:
|
||||
1. 检查是否有 `id` 参数
|
||||
2. 有 `id` 则更新,无 `id` 则新增
|
||||
3. 自动设置 `create_time` 和 `update_time`
|
||||
|
||||
### RosterLogic::batchSave()
|
||||
|
||||
批量保存逻辑:
|
||||
1. 开启事务
|
||||
2. 遍历排班数组
|
||||
3. 检查是否已存在(根据 doctor_id + date + period)
|
||||
4. 存在则更新,不存在则新增
|
||||
5. 提交事务
|
||||
|
||||
### RosterLogic::copy()
|
||||
|
||||
复制排班逻辑:
|
||||
1. 获取源日期范围的排班数据
|
||||
2. 计算日期差
|
||||
3. 为每条排班创建新记录,日期加上日期差
|
||||
4. 检查目标日期是否已存在排班,避免重复
|
||||
|
||||
## 数据验证
|
||||
|
||||
### RosterValidate
|
||||
|
||||
验证规则:
|
||||
- `doctor_id`: 必填
|
||||
- `date`: 必填,日期格式
|
||||
- `period`: 必填,只能是 morning 或 afternoon
|
||||
- `status`: 必填,只能是 1/2/3/4
|
||||
- `quota`: 数字
|
||||
- `max_patients`: 数字
|
||||
|
||||
## 模型特性
|
||||
|
||||
### Roster Model
|
||||
|
||||
自动属性:
|
||||
- `status_desc`: 状态描述(出诊/停诊/休息/请假)
|
||||
- `period_desc`: 时段描述(上午/下午)
|
||||
- `date_text`: 日期文本
|
||||
|
||||
软删除:
|
||||
- 使用 `delete_time` 字段
|
||||
- 删除时不会真正删除记录,只是设置删除时间
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **唯一索引**: `doctor_id + date + period + delete_time` 确保同一医生同一天同一时段只有一条有效排班
|
||||
|
||||
2. **医生数据来源**: 医生数据来自 `zyt_admin` 表,`role_id=1`
|
||||
|
||||
3. **时间格式**:
|
||||
- 数据库存储使用整型时间戳
|
||||
- 日期字段使用 DATE 类型
|
||||
|
||||
4. **事务处理**: 批量操作和复制操作使用事务确保数据一致性
|
||||
|
||||
5. **错误处理**: 所有 Logic 方法都有异常捕获,失败时返回 false 并设置错误信息
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. 测试新增排班
|
||||
2. 测试更新排班
|
||||
3. 测试删除排班
|
||||
4. 测试同一时段重复排班(应该失败)
|
||||
5. 测试批量保存
|
||||
6. 测试复制排班
|
||||
7. 测试日期范围查询
|
||||
@@ -0,0 +1,142 @@
|
||||
# 医生排班日期范围搜索修复
|
||||
|
||||
## 问题描述
|
||||
|
||||
访问 `adminapi/doctor.roster/lists?start_date=2026-03-02&end_date=2026-03-08` 时,`searchWhere` 为空,无法按日期范围筛选排班数据。
|
||||
|
||||
## 根本原因
|
||||
|
||||
`ListsSearchTrait` 中的 `between` 搜索类型只支持固定的参数名 `start` 和 `end`,而不支持自定义参数名如 `start_date` 和 `end_date`。
|
||||
|
||||
### 原始代码问题
|
||||
|
||||
```php
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'=' => ['doctor_id', 'period', 'status'],
|
||||
'between' => ['date' => ['start_date', 'end_date']], // ❌ 不会生效
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
`ListsSearchTrait` 中的 `between` 处理逻辑:
|
||||
|
||||
```php
|
||||
case 'between':
|
||||
if (empty($this->start) || empty($this->end)) { // 只检查 start 和 end
|
||||
break;
|
||||
}
|
||||
$where[] = [$whereFields, 'between', [$this->start, $this->end]];
|
||||
break;
|
||||
```
|
||||
|
||||
## 解决方案
|
||||
|
||||
在 `lists()` 和 `count()` 方法中手动处理日期范围搜索。
|
||||
|
||||
### 修复后的代码
|
||||
|
||||
```php
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'=' => ['doctor_id', 'period', 'status'],
|
||||
];
|
||||
}
|
||||
|
||||
public function lists(): array
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
$lists = Roster::where($where)
|
||||
->field(['id', 'doctor_id', 'date', 'period', 'status', 'quota', 'max_patients', 'booked_count', 'remark', 'create_time', 'update_time'])
|
||||
->order(['date' => 'asc', 'period' => 'asc'])
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
return Roster::where($where)->count();
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
### 测试 URL
|
||||
|
||||
```
|
||||
GET /adminapi/doctor.roster/lists?start_date=2026-03-02&end_date=2026-03-08
|
||||
```
|
||||
|
||||
### 预期结果
|
||||
|
||||
- ✅ 返回 2026-03-02 到 2026-03-08 之间的排班数据
|
||||
- ✅ `where` 条件包含日期范围:`['date', 'between', ['2026-03-02', '2026-03-08']]`
|
||||
|
||||
### 其他搜索参数
|
||||
|
||||
```
|
||||
GET /adminapi/doctor.roster/lists?doctor_id=1&start_date=2026-03-02&end_date=2026-03-08
|
||||
GET /adminapi/doctor.roster/lists?period=1&status=1&start_date=2026-03-02&end_date=2026-03-08
|
||||
```
|
||||
|
||||
## 其他说明
|
||||
|
||||
### ListsSearchTrait 支持的搜索类型
|
||||
|
||||
1. **等值查询**:`=`, `<>`, `>`, `>=`, `<`, `<=`, `in`
|
||||
2. **模糊查询**:`%like%`, `%like`, `like%`
|
||||
3. **时间范围**:`between_time`(使用 `start_time` 和 `end_time`)
|
||||
4. **范围查询**:`between`(使用 `start` 和 `end`)
|
||||
5. **集合查询**:`find_in_set`
|
||||
|
||||
### 如果要使用框架的 between
|
||||
|
||||
需要修改前端传递的参数名:
|
||||
|
||||
```javascript
|
||||
// 前端修改
|
||||
params: {
|
||||
start: '2026-03-02', // 改为 start
|
||||
end: '2026-03-08' // 改为 end
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
// 后端配置
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'=' => ['doctor_id', 'period', 'status'],
|
||||
'between' => 'date', // 简化配置
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
但这种方式不够语义化,不推荐使用。
|
||||
|
||||
## 已修复的文件
|
||||
|
||||
- ✅ `server/app/adminapi/lists/doctor/RosterLists.php`
|
||||
|
||||
---
|
||||
|
||||
**修复日期:** 2024-03-04
|
||||
**状态:** ✅ 完成
|
||||
@@ -0,0 +1,395 @@
|
||||
# 批量排班接口测试
|
||||
|
||||
## 接口信息
|
||||
|
||||
**URL**: `POST /adminapi/doctor.roster/batchSave`
|
||||
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
## 测试用例
|
||||
|
||||
### 测试1:批量创建工作日排班
|
||||
|
||||
为2位医生创建一周工作日的排班(周一至周五,上午和下午)。
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": "工作日排班"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-02",
|
||||
"period": "afternoon",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": "工作日排班"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-03",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": "工作日排班"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-03",
|
||||
"period": "afternoon",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": "工作日排班"
|
||||
},
|
||||
{
|
||||
"doctor_id": 2,
|
||||
"date": "2026-03-02",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 15,
|
||||
"max_patients": 25,
|
||||
"remark": "工作日排班"
|
||||
},
|
||||
{
|
||||
"doctor_id": 2,
|
||||
"date": "2026-03-02",
|
||||
"period": "afternoon",
|
||||
"status": 1,
|
||||
"quota": 15,
|
||||
"max_patients": 25,
|
||||
"remark": "工作日排班"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "批量保存成功",
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
### 测试2:批量设置周末休息
|
||||
|
||||
为所有医生设置周末休息。
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-07",
|
||||
"period": "morning",
|
||||
"status": 3,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "周末休息"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-07",
|
||||
"period": "afternoon",
|
||||
"status": 3,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "周末休息"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-08",
|
||||
"period": "morning",
|
||||
"status": 3,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "周末休息"
|
||||
},
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-08",
|
||||
"period": "afternoon",
|
||||
"status": 3,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "周末休息"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 测试3:更新已存在的排班
|
||||
|
||||
如果某个排班已存在,应该更新而不是创建新记录。
|
||||
|
||||
**第一次请求**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**第二次请求(更新)**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 2,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "临时停诊"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**: 第二次请求应该更新第一次创建的记录,而不是创建新记录。
|
||||
|
||||
### 测试4:空数组
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"rosters": []
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**: 应该返回成功,但不创建任何记录。
|
||||
|
||||
### 测试5:数据验证
|
||||
|
||||
**请求体(缺少必填字段)**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04"
|
||||
// 缺少 period 和 status
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**: 应该返回验证错误。
|
||||
|
||||
## 使用 cURL 测试
|
||||
|
||||
### 基本测试
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/adminapi/doctor.roster/batchSave \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token-here" \
|
||||
-d '{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### 批量测试(10条记录)
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/adminapi/doctor.roster/batchSave \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token-here" \
|
||||
-d @batch_roster_data.json
|
||||
```
|
||||
|
||||
**batch_roster_data.json**:
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{"doctor_id": 1, "date": "2026-03-04", "period": "morning", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-04", "period": "afternoon", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-05", "period": "morning", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-05", "period": "afternoon", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-06", "period": "morning", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-06", "period": "afternoon", "status": 1, "quota": 20, "max_patients": 30},
|
||||
{"doctor_id": 1, "date": "2026-03-07", "period": "morning", "status": 3, "quota": 0, "max_patients": 0},
|
||||
{"doctor_id": 1, "date": "2026-03-07", "period": "afternoon", "status": 3, "quota": 0, "max_patients": 0},
|
||||
{"doctor_id": 1, "date": "2026-03-08", "period": "morning", "status": 3, "quota": 0, "max_patients": 0},
|
||||
{"doctor_id": 1, "date": "2026-03-08", "period": "afternoon", "status": 3, "quota": 0, "max_patients": 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 验证结果
|
||||
|
||||
### 1. 检查数据库
|
||||
|
||||
```sql
|
||||
-- 查看批量创建的排班
|
||||
SELECT * FROM la_doctor_roster
|
||||
WHERE doctor_id = 1
|
||||
AND date BETWEEN '2026-03-04' AND '2026-03-08'
|
||||
ORDER BY date, period;
|
||||
```
|
||||
|
||||
### 2. 检查唯一索引
|
||||
|
||||
```sql
|
||||
-- 尝试插入重复记录,应该失败
|
||||
INSERT INTO la_doctor_roster (doctor_id, date, period, status, create_time, update_time)
|
||||
VALUES (1, '2026-03-04', 'morning', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||||
-- 错误: Duplicate entry '1-2026-03-04-morning' for key 'uk_doctor_date_period'
|
||||
```
|
||||
|
||||
### 3. 检查更新逻辑
|
||||
|
||||
```sql
|
||||
-- 第一次插入
|
||||
-- 第二次用相同的 doctor_id, date, period 但不同的 status
|
||||
-- 应该更新而不是插入新记录
|
||||
SELECT COUNT(*) FROM la_doctor_roster
|
||||
WHERE doctor_id = 1 AND date = '2026-03-04' AND period = 'morning';
|
||||
-- 应该返回 1,不是 2
|
||||
```
|
||||
|
||||
## 性能测试
|
||||
|
||||
### 测试100条记录
|
||||
|
||||
```bash
|
||||
# 生成100条记录的JSON
|
||||
php generate_test_data.php 100 > test_100.json
|
||||
|
||||
# 测试
|
||||
time curl -X POST http://your-domain/adminapi/doctor.roster/batchSave \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token-here" \
|
||||
-d @test_100.json
|
||||
```
|
||||
|
||||
**预期**: 应该在2秒内完成
|
||||
|
||||
### 测试500条记录
|
||||
|
||||
```bash
|
||||
php generate_test_data.php 500 > test_500.json
|
||||
|
||||
time curl -X POST http://your-domain/adminapi/doctor.roster/batchSave \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token-here" \
|
||||
-d @test_500.json
|
||||
```
|
||||
|
||||
**预期**: 应该在10秒内完成
|
||||
|
||||
## 错误处理测试
|
||||
|
||||
### 1. 事务回滚测试
|
||||
|
||||
在批量数据中插入一条错误数据,整个批量操作应该回滚。
|
||||
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
},
|
||||
{
|
||||
"doctor_id": 999999,
|
||||
"date": "2026-03-04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**预期**: 如果第二条记录失败,第一条也不应该被保存。
|
||||
|
||||
### 2. 日期格式错误
|
||||
|
||||
```json
|
||||
{
|
||||
"rosters": [
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026/03/04",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**预期**: 返回日期格式错误
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 问题1: 批量保存失败
|
||||
|
||||
**检查**:
|
||||
1. 数据库连接是否正常
|
||||
2. 表是否存在
|
||||
3. 字段类型是否匹配
|
||||
4. 唯一索引是否正确
|
||||
|
||||
### 问题2: 部分记录保存失败
|
||||
|
||||
**检查**:
|
||||
1. 查看错误日志
|
||||
2. 检查是否有数据验证失败
|
||||
3. 检查是否有外键约束
|
||||
|
||||
### 问题3: 性能问题
|
||||
|
||||
**优化**:
|
||||
1. 减少单次批量的记录数
|
||||
2. 添加数据库索引
|
||||
3. 使用批量插入而不是循环插入
|
||||
|
||||
## 总结
|
||||
|
||||
批量排班接口已经完整实现,包括:
|
||||
- ✅ 批量创建排班
|
||||
- ✅ 批量更新排班
|
||||
- ✅ 事务处理
|
||||
- ✅ 数据验证
|
||||
- ✅ 错误处理
|
||||
|
||||
可以正常使用了!
|
||||
@@ -0,0 +1,101 @@
|
||||
# 排班接口测试
|
||||
|
||||
## 测试前准备
|
||||
|
||||
1. 确保数据库已创建 `la_doctor_roster` 表
|
||||
2. 确保 `zyt_admin` 表中有 `role_id=1` 的用户(医生)
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 测试获取排班列表
|
||||
|
||||
```bash
|
||||
GET /adminapi/doctor.roster/lists?start_date=2026-03-02&end_date=2026-03-08
|
||||
```
|
||||
|
||||
预期结果:返回空列表或已有的排班数据
|
||||
|
||||
### 2. 测试新增排班
|
||||
|
||||
```bash
|
||||
POST /adminapi/doctor.roster/save
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-03",
|
||||
"period": "morning",
|
||||
"status": 1,
|
||||
"quota": 20,
|
||||
"max_patients": 30,
|
||||
"remark": "测试排班"
|
||||
}
|
||||
```
|
||||
|
||||
预期结果:返回成功,包含新增的排班ID
|
||||
|
||||
### 3. 测试更新排班
|
||||
|
||||
```bash
|
||||
POST /adminapi/doctor.roster/save
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"doctor_id": 1,
|
||||
"date": "2026-03-03",
|
||||
"period": "morning",
|
||||
"status": 2,
|
||||
"quota": 0,
|
||||
"max_patients": 0,
|
||||
"remark": "临时停诊"
|
||||
}
|
||||
```
|
||||
|
||||
预期结果:返回成功,排班状态更新为停诊
|
||||
|
||||
### 4. 测试删除排班
|
||||
|
||||
```bash
|
||||
POST /adminapi/doctor.roster/delete
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1
|
||||
}
|
||||
```
|
||||
|
||||
预期结果:返回成功,排班被软删除
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 控制器不存在
|
||||
|
||||
错误:`控制器不存在:\\app\\adminapi\\controller\\doctor\\RosterController`
|
||||
|
||||
解决:确保文件路径正确,文件名大小写匹配
|
||||
|
||||
### 2. 类不存在
|
||||
|
||||
错误:`Class 'app\common\model\doctor\Roster' not found`
|
||||
|
||||
解决:检查 Model 文件是否创建,命名空间是否正确
|
||||
|
||||
### 3. 表不存在
|
||||
|
||||
错误:`Table 'database.la_doctor_roster' doesn't exist`
|
||||
|
||||
解决:执行 SQL 创建表
|
||||
|
||||
### 4. 唯一索引冲突
|
||||
|
||||
错误:`Duplicate entry '1-2026-03-03-morning' for key 'uk_doctor_date_period'`
|
||||
|
||||
说明:同一医生同一天同一时段已有排班,这是正常的约束
|
||||
|
||||
## 调试技巧
|
||||
|
||||
1. 查看日志文件:`server/runtime/log/`
|
||||
2. 开启调试模式:`.env` 中设置 `APP_DEBUG=true`
|
||||
3. 使用 Postman 或类似工具测试接口
|
||||
4. 检查数据库表结构是否正确
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\controller\doctor;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\doctor\AppointmentLists;
|
||||
use app\adminapi\logic\doctor\AppointmentLogic;
|
||||
use app\adminapi\validate\doctor\AppointmentValidate;
|
||||
|
||||
/**
|
||||
* 医生预约控制器
|
||||
* Class AppointmentController
|
||||
* @package app\adminapi\controller\doctor
|
||||
*/
|
||||
class AppointmentController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function availableSlots()
|
||||
{
|
||||
$params = (new AppointmentValidate())->goCheck('availableSlots');
|
||||
$result = AppointmentLogic::getAvailableSlots($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 创建预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('create');
|
||||
$result = AppointmentLogic::create($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('预约成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('cancel');
|
||||
$result = AppointmentLogic::cancel($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('取消成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new AppointmentLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new AppointmentValidate())->goCheck('detail');
|
||||
$result = AppointmentLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生可用号源数
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function doctorAvailability()
|
||||
{
|
||||
$doctorId = $this->request->get('doctor_id');
|
||||
$date = $this->request->get('date');
|
||||
|
||||
if (!$doctorId || !$date) {
|
||||
return $this->fail('参数错误');
|
||||
}
|
||||
|
||||
$availableCount = AppointmentLogic::getDoctorAvailability($doctorId, $date);
|
||||
return $this->data(['available_count' => $availableCount]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function complete()
|
||||
{
|
||||
$params = (new AppointmentValidate())->post()->goCheck('complete');
|
||||
$result = AppointmentLogic::complete($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(AppointmentLogic::getError());
|
||||
}
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\controller\doctor;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\doctor\RosterLists;
|
||||
use app\adminapi\logic\doctor\RosterLogic;
|
||||
use app\adminapi\validate\doctor\RosterValidate;
|
||||
|
||||
/**
|
||||
* 医生排班控制器
|
||||
* Class RosterController
|
||||
* @package app\adminapi\controller\doctor
|
||||
*/
|
||||
class RosterController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 排班列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new RosterLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 保存排班(新增或更新)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('save');
|
||||
$result = RosterLogic::save($params);
|
||||
return $this->success('保存成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('delete');
|
||||
RosterLogic::delete($params);
|
||||
return $this->success('删除成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 排班详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new RosterValidate())->goCheck('detail');
|
||||
$result = RosterLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function batchSave()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('batchSave');
|
||||
$result = RosterLogic::batchSave($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(RosterLogic::getError());
|
||||
}
|
||||
return $this->success('批量保存成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$params = (new RosterValidate())->post()->goCheck('copy');
|
||||
$result = RosterLogic::copy($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(RosterLogic::getError());
|
||||
}
|
||||
return $this->success('复制成功', []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\controller\tcm;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\logic\tcm\BloodRecordLogic;
|
||||
|
||||
/**
|
||||
* 血糖血压记录控制器
|
||||
* Class BloodRecordController
|
||||
* @package app\adminapi\controller\tcm
|
||||
*/
|
||||
class BloodRecordController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 添加记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::add($params);
|
||||
if ($result) {
|
||||
return $this->success('添加成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::edit($params);
|
||||
if ($result) {
|
||||
return $this->success('编辑成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = BloodRecordLogic::delete($params);
|
||||
if ($result) {
|
||||
return $this->success('删除成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(BloodRecordLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 记录详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
$result = BloodRecordLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者的记录列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getRecordsByPatient()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
$result = BloodRecordLogic::getRecordsByPatient($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\controller\tcm;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\tcm\DiagnosisLists;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\adminapi\validate\tcm\DiagnosisValidate;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单控制器
|
||||
* Class DiagnosisController
|
||||
* @package app\adminapi\controller\tcm
|
||||
*/
|
||||
class DiagnosisController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* @notes 测试接口
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
return $this->success('中医诊单控制器可以访问', [
|
||||
'controller' => 'TcmDiagnosisController',
|
||||
'namespace' => __NAMESPACE__,
|
||||
'class' => __CLASS__,
|
||||
'method' => __METHOD__
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊单列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
return $this->dataLists(new DiagnosisLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 添加诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('add');
|
||||
$result = DiagnosisLogic::add($params);
|
||||
if ($result) {
|
||||
return $this->success('添加成功', ['id' => $result], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('edit');
|
||||
$result = DiagnosisLogic::edit($params);
|
||||
if ($result) {
|
||||
return $this->success('编辑成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除诊单
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->post()->goCheck('id');
|
||||
$result = DiagnosisLogic::delete($params);
|
||||
if ($result) {
|
||||
return $this->success('删除成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊单详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$params = (new DiagnosisValidate())->goCheck('id');
|
||||
$result = DiagnosisLogic::detail($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查手机号是否重复
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function checkPhone()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = DiagnosisLogic::checkPhone($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查身份证号是否重复
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function checkIdCard()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
$result = DiagnosisLogic::checkIdCard($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 指派医助
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function assign()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
// 验证参数
|
||||
if (empty($params['id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
if (!isset($params['assistant_id'])) {
|
||||
return $this->fail('请选择医助');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::assign($params);
|
||||
if ($result) {
|
||||
return $this->success('指派成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话签名
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getCallSignature()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
if (empty($params['patient_id'])) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::getCallSignature($params);
|
||||
if ($result) {
|
||||
return $this->data($result);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发起通话
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function startCall()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::startCall($params);
|
||||
if ($result) {
|
||||
return $this->success('发起通话成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 结束通话
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function endCall()
|
||||
{
|
||||
$params = $this->request->post();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
// 传递当前管理员ID
|
||||
$params['admin_id'] = $this->adminId;
|
||||
|
||||
$result = DiagnosisLogic::endCall($params);
|
||||
if ($result) {
|
||||
return $this->success('结束通话成功', [], 1, 1);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话记录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getCallRecords()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
|
||||
if (empty($params['diagnosis_id'])) {
|
||||
return $this->fail('诊单ID不能为空');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::getCallRecords($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者通话签名(用于测试)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getPatientSignature()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
|
||||
if (empty($params['patient_id'])) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::getPatientSignature((int)$params['patient_id']);
|
||||
if ($result) {
|
||||
return $this->data($result);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医助列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getAssistants()
|
||||
{
|
||||
$result = DiagnosisLogic::getAssistants();
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getDoctors()
|
||||
{
|
||||
$result = DiagnosisLogic::getDoctors();
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\lists\doctor;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 医生预约列表
|
||||
* Class AppointmentLists
|
||||
* @package app\adminapi\lists\doctor
|
||||
*/
|
||||
class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
$allowSearch = [
|
||||
'=' => ['a.patient_id', 'a.doctor_id', 'a.status', 'a.appointment_date'],
|
||||
];
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$this->searchWhere[] = ['a.appointment_date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
// 处理患者姓名搜索
|
||||
if (!empty($this->params['patient_name'])) {
|
||||
$this->searchWhere[] = ['u.patient_name', 'like', '%' . $this->params['patient_name'] . '%'];
|
||||
}
|
||||
|
||||
// 处理医生姓名搜索
|
||||
if (!empty($this->params['doctor_name'])) {
|
||||
$this->searchWhere[] = ['ad.name', 'like', '%' . $this->params['doctor_name'] . '%'];
|
||||
}
|
||||
|
||||
return $allowSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
|
||||
$lists = Appointment::alias('a')
|
||||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||||
->leftJoin('admin ad', 'a.doctor_id = ad.id')
|
||||
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, ad.name as doctor_name')
|
||||
->where($this->searchWhere)
|
||||
->order('a.id', 'desc')
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 添加状态和类型描述
|
||||
foreach ($lists as &$item) {
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
$item['status_desc'] = $statusMap[$item['status']] ?? '未知';
|
||||
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
$item['appointment_type_desc'] = $typeMap[$item['appointment_type']] ?? '未知';
|
||||
|
||||
// 格式化时间戳为日期时间
|
||||
if (isset($item['create_time']) && is_numeric($item['create_time'])) {
|
||||
$item['create_time'] = date('Y-m-d H:i:s', $item['create_time']);
|
||||
}
|
||||
if (isset($item['update_time']) && is_numeric($item['update_time'])) {
|
||||
$item['update_time'] = date('Y-m-d H:i:s', $item['update_time']);
|
||||
}
|
||||
}
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$query = Appointment::alias('a')
|
||||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||||
->leftJoin('admin ad', 'a.doctor_id = ad.id')
|
||||
->where($this->searchWhere);
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\lists\doctor;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\doctor\Roster;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 医生排班列表
|
||||
* Class RosterLists
|
||||
* @package app\adminapi\lists\doctor
|
||||
*/
|
||||
class RosterLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'=' => ['doctor_id', 'period', 'status'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
$lists = Roster::where($where)
|
||||
->field(['id', 'doctor_id', 'date', 'period', 'status', 'quota', 'max_patients', 'booked_count', 'remark', 'create_time', 'update_time'])
|
||||
->order(['date' => 'asc', 'period' => 'asc'])
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理日期范围搜索
|
||||
if (!empty($this->params['start_date']) && !empty($this->params['end_date'])) {
|
||||
$where[] = ['date', 'between', [$this->params['start_date'], $this->params['end_date']]];
|
||||
}
|
||||
|
||||
return Roster::where($where)->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\lists\tcm;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单列表
|
||||
* Class DiagnosisLists
|
||||
* @package app\adminapi\lists\tcm
|
||||
*/
|
||||
class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
*/
|
||||
public function setSearch(): array
|
||||
{
|
||||
return [
|
||||
'%like%' => ['patient_name'],
|
||||
'=' => ['patient_id', 'gender', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status'],
|
||||
'between_time' => ['diagnosis_date'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function lists(): array
|
||||
{
|
||||
$lists = Diagnosis::where($this->searchWhere)
|
||||
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status', 'create_time', 'update_time'])
|
||||
->append(['gender_desc', 'status_desc', 'diagnosis_date_text'])
|
||||
->order(['id' => 'desc'])
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取数量
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return Diagnosis::where($this->searchWhere)->count();
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,10 @@ use app\common\model\auth\AdminRole;
|
||||
use app\common\model\auth\AdminSession;
|
||||
use app\common\cache\AdminTokenCache;
|
||||
use app\common\service\FileService;
|
||||
use app\common\service\TencentImService;
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 管理员逻辑
|
||||
@@ -67,6 +69,9 @@ class AdminLogic extends BaseLogic
|
||||
// 岗位
|
||||
self::insertJobs($admin['id'], $params['jobs_id'] ?? []);
|
||||
|
||||
// 导入医生账号到腾讯云IM
|
||||
self::importDoctorAccountToIm($admin['id'], $params['name']);
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -134,6 +139,9 @@ class AdminLogic extends BaseLogic
|
||||
// 岗位
|
||||
self::insertJobs($params['id'], $params['jobs_id'] ?? []);
|
||||
|
||||
// 导入医生账号到腾讯云IM
|
||||
self::importDoctorAccountToIm($params['id'], $params['name']);
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -334,4 +342,33 @@ class AdminLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 导入医生账号到腾讯云IM
|
||||
* @param int $adminId 管理员ID
|
||||
* @param string $name 管理员名称
|
||||
* @return void
|
||||
* @author AI Assistant
|
||||
* @date 2026/03/02
|
||||
*/
|
||||
public static function importDoctorAccountToIm($adminId, $name)
|
||||
{
|
||||
try {
|
||||
$userId = 'doctor_' . $adminId;
|
||||
|
||||
Log::info('开始导入医生IM账号 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', name: ' . $name);
|
||||
|
||||
$imService = new TencentImService();
|
||||
$result = $imService->importAccount($userId, $name);
|
||||
|
||||
if ($result) {
|
||||
Log::info('医生IM账号导入成功 - admin_id: ' . $adminId . ', user_id: ' . $userId);
|
||||
} else {
|
||||
Log::warning('医生IM账号导入失败 - admin_id: ' . $adminId . ', user_id: ' . $userId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('导入医生IM账号异常 - admin_id: ' . $adminId . ', error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\logic\doctor;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\doctor\Appointment;
|
||||
use app\common\model\doctor\Roster;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 医生预约逻辑
|
||||
* Class AppointmentLogic
|
||||
* @package app\adminapi\logic\doctor
|
||||
*/
|
||||
class AppointmentLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableSlots(array $params)
|
||||
{
|
||||
try {
|
||||
$doctorId = $params['doctor_id'];
|
||||
$date = $params['appointment_date'];
|
||||
$period = $params['period'] ?? 'all';
|
||||
|
||||
// 记录请求参数
|
||||
\think\facade\Log::info('获取可用时段 - 请求参数', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'period' => $period
|
||||
]);
|
||||
|
||||
// 1. 检查医生在该日期是否有出诊排班(只查询status=1的记录)
|
||||
$rosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'status' => 1 // 只查询出诊状态
|
||||
])->select()->toArray();
|
||||
|
||||
// 记录查询到的排班
|
||||
\think\facade\Log::info('查询到的排班数据', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'count' => count($rosters),
|
||||
'rosters' => $rosters
|
||||
]);
|
||||
|
||||
// 额外记录:查询该日期所有排班(用于调试)
|
||||
$allRosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date
|
||||
])->select()->toArray();
|
||||
\think\facade\Log::info('该日期所有排班数据(包括非出诊)', [
|
||||
'count' => count($allRosters),
|
||||
'all_rosters' => $allRosters
|
||||
]);
|
||||
|
||||
// 如果没有出诊排班,返回空数组
|
||||
if (empty($rosters)) {
|
||||
\think\facade\Log::warning('没有找到出诊排班', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date
|
||||
]);
|
||||
return ['slots' => []];
|
||||
}
|
||||
|
||||
// 2. 根据排班时段生成时间段
|
||||
$slots = [];
|
||||
|
||||
foreach ($rosters as $roster) {
|
||||
// 再次确认状态为出诊(双重检查)
|
||||
if ($roster['status'] != 1) {
|
||||
\think\facade\Log::warning('跳过非出诊排班', [
|
||||
'roster_id' => $roster['id'],
|
||||
'status' => $roster['status']
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$periodValue = $roster['period'];
|
||||
|
||||
\think\facade\Log::info('处理排班时段', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue,
|
||||
'status' => $roster['status'],
|
||||
'period_type' => gettype($periodValue)
|
||||
]);
|
||||
|
||||
// 根据时段生成时间段(支持数字和字符串格式)
|
||||
$startHour = null;
|
||||
$endHour = null;
|
||||
|
||||
if ($periodValue == 1 || $periodValue === 'morning' || $periodValue === '上午') {
|
||||
// 上午:9:00-12:00
|
||||
$startHour = 9;
|
||||
$endHour = 12;
|
||||
\think\facade\Log::info('识别为上午时段', ['period' => $periodValue]);
|
||||
} elseif ($periodValue == 2 || $periodValue === 'afternoon' || $periodValue === '下午') {
|
||||
// 下午:14:00-18:00
|
||||
$startHour = 14;
|
||||
$endHour = 18;
|
||||
\think\facade\Log::info('识别为下午时段', ['period' => $periodValue]);
|
||||
} else {
|
||||
\think\facade\Log::warning('未知的时段值,跳过此记录', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue,
|
||||
'period_type' => gettype($periodValue)
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 确保startHour和endHour已设置
|
||||
if ($startHour === null || $endHour === null) {
|
||||
\think\facade\Log::error('时段参数未正确设置', [
|
||||
'roster_id' => $roster['id'],
|
||||
'period' => $periodValue
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 生成15分钟间隔的时间段
|
||||
for ($hour = $startHour; $hour < $endHour; $hour++) {
|
||||
for ($minute = 0; $minute < 60; $minute += 15) {
|
||||
$time = sprintf('%02d:%02d', $hour, $minute);
|
||||
|
||||
$slots[] = [
|
||||
'time' => $time,
|
||||
'available' => true,
|
||||
'quota' => 1,
|
||||
'period' => $periodValue
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\think\facade\Log::info('生成的时间段数量(去重前)', [
|
||||
'count' => count($slots),
|
||||
'sample_slots' => array_slice($slots, 0, 3)
|
||||
]);
|
||||
|
||||
// 如果没有生成任何时间段,返回空数组
|
||||
if (empty($slots)) {
|
||||
\think\facade\Log::warning('没有生成任何时间段');
|
||||
return ['slots' => []];
|
||||
}
|
||||
|
||||
// 3. 去重:按时间去重(因为可能有重复的排班记录)
|
||||
$uniqueSlots = [];
|
||||
$timeMap = [];
|
||||
foreach ($slots as $slot) {
|
||||
$time = $slot['time'];
|
||||
if (!isset($timeMap[$time])) {
|
||||
$timeMap[$time] = true;
|
||||
$uniqueSlots[] = $slot;
|
||||
}
|
||||
}
|
||||
$slots = $uniqueSlots;
|
||||
|
||||
\think\facade\Log::info('生成的时间段数量(去重后)', [
|
||||
'count' => count($slots),
|
||||
'morning_slots' => count(array_filter($slots, function($s) {
|
||||
return $s['time'] < '12:00';
|
||||
})),
|
||||
'afternoon_slots' => count(array_filter($slots, function($s) {
|
||||
return $s['time'] >= '14:00';
|
||||
})),
|
||||
'first_3_slots' => array_slice($slots, 0, 3),
|
||||
'last_3_slots' => array_slice($slots, -3)
|
||||
]);
|
||||
|
||||
// 4. 查询已预约的时间段
|
||||
$appointmentTimes = Appointment::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'appointment_date' => $date,
|
||||
'status' => 1 // 只查询有效预约
|
||||
])->column('appointment_time');
|
||||
|
||||
// 将时间格式统一为 HH:MM(去掉秒)
|
||||
$appointments = array_map(function($time) {
|
||||
// 如果是 HH:MM:SS 格式,截取前5位
|
||||
return substr($time, 0, 5);
|
||||
}, $appointmentTimes);
|
||||
|
||||
// 4. 标记已占用的时间段
|
||||
foreach ($slots as &$slot) {
|
||||
if (in_array($slot['time'], $appointments)) {
|
||||
$slot['available'] = false;
|
||||
$slot['quota'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 按时间排序
|
||||
usort($slots, function($a, $b) {
|
||||
return strcmp($a['time'], $b['time']);
|
||||
});
|
||||
|
||||
return ['slots' => $slots];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return ['slots' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 创建预约
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function create(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
// 1. 检查时间段是否已被预约(每个时间段只能预约1次)
|
||||
// 统一时间格式为 HH:MM:SS
|
||||
$appointmentTime = strlen($params['appointment_time']) == 5
|
||||
? $params['appointment_time'] . ':00'
|
||||
: $params['appointment_time'];
|
||||
|
||||
$exists = Appointment::where([
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'status' => 1
|
||||
])->where('appointment_time', $appointmentTime)
|
||||
->find();
|
||||
|
||||
if ($exists) {
|
||||
self::setError('该时间段已被预约');
|
||||
Db::rollback();
|
||||
return false;
|
||||
}
|
||||
$data = [
|
||||
'patient_id' => $params['patient_id'],
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'roster_id' => 0, // 不再关联排班表
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'period' => $params['period'] ?? 'all',
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $params['appointment_type'] ?? 'video',
|
||||
'remark' => $params['remark'] ?? '',
|
||||
'status' => 1,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
|
||||
$appointment = Appointment::create($data);
|
||||
|
||||
Db::commit();
|
||||
return ['id' => $appointment->id];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function cancel(array $params)
|
||||
{
|
||||
try {
|
||||
$appointment = Appointment::findOrEmpty($params['id']);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment->status = 2; // 已取消
|
||||
$appointment->update_time = time();
|
||||
$appointment->save();
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail(array $params)
|
||||
{
|
||||
$appointment = Appointment::alias('a')
|
||||
->leftJoin('la_user u', 'a.patient_id = u.id')
|
||||
->leftJoin('la_admin ad', 'a.doctor_id = ad.id')
|
||||
->field('a.*, u.nickname as patient_name, u.mobile as patient_phone, ad.name as doctor_name')
|
||||
->where('a.id', $params['id'])
|
||||
->findOrEmpty()
|
||||
->toArray();
|
||||
|
||||
if (empty($appointment)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 添加状态和类型描述
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
$appointment['status_desc'] = $statusMap[$appointment['status']] ?? '未知';
|
||||
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
$appointment['appointment_type_desc'] = $typeMap[$appointment['appointment_type']] ?? '未知';
|
||||
|
||||
// 格式化时间戳为日期时间
|
||||
if (isset($appointment['create_time']) && is_numeric($appointment['create_time'])) {
|
||||
$appointment['create_time'] = date('Y-m-d H:i:s', $appointment['create_time']);
|
||||
}
|
||||
if (isset($appointment['update_time']) && is_numeric($appointment['update_time'])) {
|
||||
$appointment['update_time'] = date('Y-m-d H:i:s', $appointment['update_time']);
|
||||
}
|
||||
|
||||
return $appointment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生某天的可用号源总数
|
||||
* @param int $doctorId
|
||||
* @param string $date
|
||||
* @return int
|
||||
*/
|
||||
public static function getDoctorAvailability($doctorId, $date)
|
||||
{
|
||||
try {
|
||||
$totalAvailable = 0;
|
||||
|
||||
// 查询上午和下午的排班
|
||||
$rosters = Roster::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => $date,
|
||||
'status' => 1 // 出诊
|
||||
])->select();
|
||||
|
||||
foreach ($rosters as $roster) {
|
||||
// 查询该时段已预约数量
|
||||
$appointmentCount = Appointment::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'appointment_date' => $date,
|
||||
'period' => $roster->period,
|
||||
'status' => 1
|
||||
])->count();
|
||||
|
||||
// 计算剩余号源
|
||||
$remaining = $roster->quota - $appointmentCount;
|
||||
if ($remaining > 0) {
|
||||
$totalAvailable += $remaining;
|
||||
}
|
||||
}
|
||||
|
||||
return $totalAvailable;
|
||||
} catch (\Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function complete(array $params)
|
||||
{
|
||||
try {
|
||||
$appointment = Appointment::findOrEmpty($params['id']);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($appointment->status != 1) {
|
||||
self::setError('该预约已处理,无法完成');
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment->status = 3; // 已完成
|
||||
$appointment->update_time = time();
|
||||
$appointment->save();
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\logic\doctor;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\doctor\Roster;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 医生排班逻辑
|
||||
* Class RosterLogic
|
||||
* @package app\adminapi\logic\doctor
|
||||
*/
|
||||
class RosterLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 保存排班
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function save(array $params)
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'date' => $params['date'],
|
||||
'period' => $params['period'],
|
||||
'status' => $params['status'],
|
||||
'quota' => $params['quota'] ?? 0,
|
||||
'max_patients' => $params['max_patients'] ?? 0,
|
||||
'remark' => $params['remark'] ?? '',
|
||||
];
|
||||
|
||||
if (isset($params['id']) && $params['id']) {
|
||||
// 更新
|
||||
$data['update_time'] = time();
|
||||
Roster::where('id', $params['id'])->update($data);
|
||||
return ['id' => $params['id']];
|
||||
} else {
|
||||
// 新增
|
||||
$data['create_time'] = time();
|
||||
$data['update_time'] = time();
|
||||
$roster = Roster::create($data);
|
||||
return ['id' => $roster->id];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除排班
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete(array $params)
|
||||
{
|
||||
try {
|
||||
Roster::destroy($params['id']);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 排班详情
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail(array $params)
|
||||
{
|
||||
return Roster::findOrEmpty($params['id'])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存排班
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function batchSave(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
$successCount = 0;
|
||||
$updateCount = 0;
|
||||
$createCount = 0;
|
||||
|
||||
foreach ($params['rosters'] as $roster) {
|
||||
$data = [
|
||||
'doctor_id' => $roster['doctor_id'],
|
||||
'date' => $roster['date'],
|
||||
'period' => $roster['period'],
|
||||
'status' => $roster['status'],
|
||||
'quota' => $roster['quota'] ?? 0,
|
||||
'max_patients' => $roster['max_patients'] ?? 0,
|
||||
'remark' => $roster['remark'] ?? '',
|
||||
];
|
||||
|
||||
// 检查是否已存在
|
||||
$exists = Roster::where([
|
||||
['doctor_id', '=', $data['doctor_id']],
|
||||
['date', '=', $data['date']],
|
||||
['period', '=', $data['period']],
|
||||
])->find();
|
||||
|
||||
if ($exists) {
|
||||
// 更新
|
||||
$data['update_time'] = time();
|
||||
$exists->save($data);
|
||||
$updateCount++;
|
||||
} else {
|
||||
// 新增
|
||||
$data['create_time'] = time();
|
||||
$data['update_time'] = time();
|
||||
Roster::create($data);
|
||||
$createCount++;
|
||||
}
|
||||
$successCount++;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return [
|
||||
'success_count' => $successCount,
|
||||
'create_count' => $createCount,
|
||||
'update_count' => $updateCount,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制排班
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function copy(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
// 获取源排班数据
|
||||
$where = [
|
||||
['date', 'between', [$params['source_start_date'], $params['source_end_date']]]
|
||||
];
|
||||
|
||||
if (isset($params['doctor_id']) && $params['doctor_id']) {
|
||||
$where[] = ['doctor_id', '=', $params['doctor_id']];
|
||||
}
|
||||
|
||||
$sourceRosters = Roster::where($where)->select();
|
||||
|
||||
// 计算日期差
|
||||
$sourceDays = (strtotime($params['source_end_date']) - strtotime($params['source_start_date'])) / 86400;
|
||||
$targetDays = (strtotime($params['target_start_date']) - strtotime($params['source_start_date'])) / 86400;
|
||||
|
||||
foreach ($sourceRosters as $roster) {
|
||||
$newDate = date('Y-m-d', strtotime($roster->date) + ($targetDays * 86400));
|
||||
|
||||
$data = [
|
||||
'doctor_id' => $roster->doctor_id,
|
||||
'date' => $newDate,
|
||||
'period' => $roster->period,
|
||||
'status' => $roster->status,
|
||||
'quota' => $roster->quota,
|
||||
'max_patients' => $roster->max_patients,
|
||||
'remark' => $roster->remark,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
|
||||
// 检查目标日期是否已存在排班
|
||||
$exists = Roster::where([
|
||||
['doctor_id', '=', $data['doctor_id']],
|
||||
['date', '=', $data['date']],
|
||||
['period', '=', $data['period']],
|
||||
])->find();
|
||||
|
||||
if (!$exists) {
|
||||
Roster::create($data);
|
||||
}
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\logic\tcm;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\tcm\BloodRecord;
|
||||
|
||||
/**
|
||||
* 血糖血压记录逻辑
|
||||
* Class BloodRecordLogic
|
||||
* @package app\adminapi\logic\tcm
|
||||
*/
|
||||
class BloodRecordLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 添加记录
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function add(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 处理记录日期
|
||||
if (isset($params['record_date'])) {
|
||||
$params['record_date'] = strtotime($params['record_date']);
|
||||
}
|
||||
|
||||
BloodRecord::create($params);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑记录
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function edit(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 处理记录日期
|
||||
if (isset($params['record_date'])) {
|
||||
$params['record_date'] = strtotime($params['record_date']);
|
||||
}
|
||||
|
||||
BloodRecord::update($params);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除记录
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete(array $params): bool
|
||||
{
|
||||
try {
|
||||
BloodRecord::destroy($params['id']);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 记录详情
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail($params): array
|
||||
{
|
||||
$record = BloodRecord::findOrEmpty($params['id'])->toArray();
|
||||
|
||||
// 处理记录日期格式
|
||||
if (!empty($record['record_date'])) {
|
||||
$record['record_date'] = date('Y-m-d', $record['record_date']);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者的血糖血压记录列表
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getRecordsByPatient(array $params): array
|
||||
{
|
||||
$where = [];
|
||||
|
||||
if (isset($params['diagnosis_id'])) {
|
||||
$where[] = ['diagnosis_id', '=', $params['diagnosis_id']];
|
||||
}
|
||||
|
||||
if (isset($params['patient_id'])) {
|
||||
$where[] = ['patient_id', '=', $params['patient_id']];
|
||||
}
|
||||
|
||||
$records = BloodRecord::where($where)
|
||||
->where('delete_time', null)
|
||||
->order('record_date', 'desc')
|
||||
->order('record_time', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 格式化日期
|
||||
foreach ($records as &$record) {
|
||||
if (!empty($record['record_date'])) {
|
||||
$record['record_date'] = date('Y-m-d', $record['record_date']);
|
||||
}
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,803 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\logic\tcm;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单逻辑
|
||||
* Class DiagnosisLogic
|
||||
* @package app\adminapi\logic\tcm
|
||||
*/
|
||||
class DiagnosisLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 添加诊单
|
||||
* @param array $params
|
||||
* @return int|bool 成功返回ID,失败返回false
|
||||
*/
|
||||
public static function add(array $params)
|
||||
{
|
||||
try {
|
||||
// 检查手机号是否重复
|
||||
if (!empty($params['phone'])) {
|
||||
$exists = Diagnosis::where('phone', $params['phone'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
self::setError('该手机号已存在');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查身份证号是否重复
|
||||
if (!empty($params['id_card'])) {
|
||||
$exists = Diagnosis::where('id_card', $params['id_card'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
self::setError('该身份证号已存在');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成患者ID
|
||||
$params['patient_id'] = self::generatePatientId();
|
||||
|
||||
// 处理既往史数组
|
||||
if (isset($params['past_history']) && is_array($params['past_history'])) {
|
||||
$params['past_history'] = implode(',', $params['past_history']);
|
||||
}
|
||||
|
||||
// 处理现病史多选字段
|
||||
$multiSelectFields = [
|
||||
'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition',
|
||||
'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition',
|
||||
'stool_condition', 'kidney_condition'
|
||||
];
|
||||
|
||||
foreach ($multiSelectFields as $field) {
|
||||
if (isset($params[$field]) && is_array($params[$field])) {
|
||||
$params[$field] = implode(',', $params[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理舌苔照片(JSON数组)
|
||||
if (isset($params['tongue_images']) && is_array($params['tongue_images'])) {
|
||||
$params['tongue_images'] = json_encode($params['tongue_images'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 处理检查报告(JSON数组)
|
||||
if (isset($params['report_files']) && is_array($params['report_files'])) {
|
||||
$params['report_files'] = json_encode($params['report_files'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 处理诊断日期
|
||||
if (isset($params['diagnosis_date'])) {
|
||||
$params['diagnosis_date'] = strtotime($params['diagnosis_date']);
|
||||
}
|
||||
|
||||
$model = Diagnosis::create($params);
|
||||
|
||||
// 自动为患者创建 TRTC 账号
|
||||
self::createPatientTrtcAccount($model->patient_id);
|
||||
|
||||
return $model->id;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成患者ID
|
||||
* @return int
|
||||
*/
|
||||
private static function generatePatientId(): int
|
||||
{
|
||||
// 获取当前最大的患者ID
|
||||
$maxPatientId = Diagnosis::max('patient_id') ?? 100000;
|
||||
|
||||
// 返回下一个患者ID
|
||||
return $maxPatientId + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 编辑诊单
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function edit(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 检查手机号是否重复(排除当前记录)
|
||||
if (!empty($params['phone'])) {
|
||||
$exists = Diagnosis::where('phone', $params['phone'])
|
||||
->where('id', '<>', $params['id'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
self::setError('该手机号已存在');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查身份证号是否重复(排除当前记录)
|
||||
if (!empty($params['id_card'])) {
|
||||
$exists = Diagnosis::where('id_card', $params['id_card'])
|
||||
->where('id', '<>', $params['id'])
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
self::setError('该身份证号已存在');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理既往史数组
|
||||
if (isset($params['past_history']) && is_array($params['past_history'])) {
|
||||
$params['past_history'] = implode(',', $params['past_history']);
|
||||
}
|
||||
|
||||
// 处理现病史多选字段
|
||||
$multiSelectFields = [
|
||||
'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition',
|
||||
'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition',
|
||||
'stool_condition', 'kidney_condition'
|
||||
];
|
||||
|
||||
foreach ($multiSelectFields as $field) {
|
||||
if (isset($params[$field]) && is_array($params[$field])) {
|
||||
$params[$field] = implode(',', $params[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理舌苔照片(JSON数组)
|
||||
if (isset($params['tongue_images']) && is_array($params['tongue_images'])) {
|
||||
$params['tongue_images'] = json_encode($params['tongue_images'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 处理检查报告(JSON数组)
|
||||
if (isset($params['report_files']) && is_array($params['report_files'])) {
|
||||
$params['report_files'] = json_encode($params['report_files'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 处理诊断日期
|
||||
if (isset($params['diagnosis_date'])) {
|
||||
$params['diagnosis_date'] = strtotime($params['diagnosis_date']);
|
||||
}
|
||||
|
||||
Diagnosis::update($params);
|
||||
|
||||
// 如果是编辑,也确保患者有 TRTC 账号
|
||||
$diagnosis = Diagnosis::find($params['id']);
|
||||
if ($diagnosis) {
|
||||
self::createPatientTrtcAccount($diagnosis->patient_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除诊单
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete(array $params): bool
|
||||
{
|
||||
try {
|
||||
Diagnosis::destroy($params['id']);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊单详情
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public static function detail($params): array
|
||||
{
|
||||
$diagnosis = Diagnosis::findOrEmpty($params['id'])->toArray();
|
||||
|
||||
// 处理既往史为数组
|
||||
if (!empty($diagnosis['past_history'])) {
|
||||
$diagnosis['past_history'] = explode(',', $diagnosis['past_history']);
|
||||
} else {
|
||||
$diagnosis['past_history'] = [];
|
||||
}
|
||||
|
||||
// 处理现病史多选字段为数组
|
||||
$multiSelectFields = [
|
||||
'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition',
|
||||
'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition',
|
||||
'stool_condition', 'kidney_condition'
|
||||
];
|
||||
|
||||
foreach ($multiSelectFields as $field) {
|
||||
if (!empty($diagnosis[$field])) {
|
||||
$diagnosis[$field] = explode(',', $diagnosis[$field]);
|
||||
} else {
|
||||
$diagnosis[$field] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 处理舌苔照片(JSON转数组)
|
||||
if (!empty($diagnosis['tongue_images'])) {
|
||||
$diagnosis['tongue_images'] = json_decode($diagnosis['tongue_images'], true) ?: [];
|
||||
} else {
|
||||
$diagnosis['tongue_images'] = [];
|
||||
}
|
||||
|
||||
// 处理检查报告(JSON转数组)
|
||||
if (!empty($diagnosis['report_files'])) {
|
||||
$diagnosis['report_files'] = json_decode($diagnosis['report_files'], true) ?: [];
|
||||
} else {
|
||||
$diagnosis['report_files'] = [];
|
||||
}
|
||||
|
||||
// 处理诊断日期格式
|
||||
if (!empty($diagnosis['diagnosis_date'])) {
|
||||
$diagnosis['diagnosis_date'] = date('Y-m-d', $diagnosis['diagnosis_date']);
|
||||
}
|
||||
|
||||
return $diagnosis;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查手机号是否重复
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function checkPhone(array $params): array
|
||||
{
|
||||
$query = Diagnosis::where('phone', $params['phone'])
|
||||
->where('delete_time', null);
|
||||
|
||||
// 编辑时排除当前记录
|
||||
if (isset($params['id']) && $params['id']) {
|
||||
$query->where('id', '<>', $params['id']);
|
||||
}
|
||||
|
||||
$exists = $query->find();
|
||||
|
||||
return [
|
||||
'exists' => !empty($exists),
|
||||
'message' => $exists ? '该手机号已存在' : ''
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查身份证号是否重复
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function checkIdCard(array $params): array
|
||||
{
|
||||
$query = Diagnosis::where('id_card', $params['id_card'])
|
||||
->where('delete_time', null);
|
||||
|
||||
// 编辑时排除当前记录
|
||||
if (isset($params['id']) && $params['id']) {
|
||||
$query->where('id', '<>', $params['id']);
|
||||
}
|
||||
|
||||
$exists = $query->find();
|
||||
|
||||
return [
|
||||
'exists' => !empty($exists),
|
||||
'message' => $exists ? '该身份证号已存在' : ''
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 指派医助
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function assign(array $params): bool
|
||||
{
|
||||
try {
|
||||
Diagnosis::where('id', $params['id'])->update([
|
||||
'assistant_id' => $params['assistant_id']
|
||||
]);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话签名
|
||||
* @param array $params
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function getCallSignature(array $params)
|
||||
{
|
||||
try {
|
||||
// 获取配置
|
||||
$config = self::getTrtcConfig();
|
||||
|
||||
if (!$config) {
|
||||
self::setError('请先配置腾讯云TRTC参数');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前管理员ID(从参数中获取)
|
||||
$adminId = $params['admin_id'] ?? 0;
|
||||
$patientId = $params['patient_id'] ?? 0;
|
||||
|
||||
if (!$adminId) {
|
||||
self::setError('获取管理员信息失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$patientId) {
|
||||
self::setError('获取患者信息失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 医生userId
|
||||
$doctorUserId = 'doctor_' . $adminId;
|
||||
|
||||
// 患者userId(必须与小程序端一致)
|
||||
$patientUserId = 'patient_' . $patientId;
|
||||
|
||||
// 生成医生的 UserSig
|
||||
$userSig = self::generateUserSig($config['sdkAppId'], $config['secretKey'], $doctorUserId);
|
||||
|
||||
if (!$userSig) {
|
||||
self::setError('生成签名失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 导入医生账号到IM
|
||||
self::importDoctorAccountToIm($adminId, $doctorUserId);
|
||||
|
||||
// 确保患者账号也已导入IM(用于跨平台通话)
|
||||
self::ensurePatientImAccount($patientId, $patientUserId);
|
||||
|
||||
return [
|
||||
'sdkAppId' => (int)$config['sdkAppId'], // 确保返回整数
|
||||
'userId' => $doctorUserId, // 医生的userId
|
||||
'userSig' => $userSig,
|
||||
'patientUserId' => $patientUserId, // 患者的userId(用于发起通话)
|
||||
'expireTime' => 86400 // 24小时
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 确保患者IM账号存在
|
||||
* @param int $patientId
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
*/
|
||||
private static function ensurePatientImAccount(int $patientId, string $userId): bool
|
||||
{
|
||||
try {
|
||||
// 获取患者信息
|
||||
$diagnosis = Diagnosis::where('patient_id', $patientId)->find();
|
||||
$nick = $diagnosis ? $diagnosis->patient_name : '患者' . $patientId;
|
||||
|
||||
// 调用IM服务导入账号
|
||||
$imService = new \app\common\service\TencentImService();
|
||||
$result = $imService->importAccount($userId, $nick);
|
||||
|
||||
if ($result['success']) {
|
||||
\think\facade\Log::info('确保患者IM账号存在 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', nick: ' . $nick);
|
||||
return true;
|
||||
} else {
|
||||
\think\facade\Log::warning('导入患者IM账号失败 - patient_id: ' . $patientId . ', error: ' . ($result['message'] ?? '未知错误'));
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('确保患者IM账号失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 导入医生账号到腾讯云IM
|
||||
* @param int $adminId
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
*/
|
||||
private static function importDoctorAccountToIm(int $adminId, string $userId): bool
|
||||
{
|
||||
try {
|
||||
// 获取医生信息
|
||||
$admin = \app\common\model\auth\Admin::find($adminId);
|
||||
$nick = $admin ? $admin->name : '医生' . $adminId;
|
||||
|
||||
// 调用IM服务导入账号
|
||||
$imService = new \app\common\service\TencentImService();
|
||||
$result = $imService->importAccount($userId, $nick);
|
||||
|
||||
if ($result['success']) {
|
||||
\think\facade\Log::info('导入医生IM账号成功 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', nick: ' . $nick);
|
||||
return true;
|
||||
} else {
|
||||
\think\facade\Log::warning('导入医生IM账号失败 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', error: ' . $result['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('导入医生IM账号异常 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发起通话
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function startCall(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 获取当前管理员ID(从参数中获取)
|
||||
$adminId = $params['admin_id'] ?? 0;
|
||||
|
||||
if (!$adminId) {
|
||||
self::setError('获取管理员信息失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建通话记录
|
||||
\app\common\model\tcm\CallRecord::create([
|
||||
'diagnosis_id' => $params['diagnosis_id'],
|
||||
'caller_id' => $adminId,
|
||||
'caller_type' => 'doctor',
|
||||
'callee_id' => $params['patient_id'] ?? 0,
|
||||
'callee_type' => 'patient',
|
||||
'call_type' => $params['call_type'] ?? 2, // 1-语音 2-视频
|
||||
'status' => 1, // 1-进行中
|
||||
'start_time' => time()
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 结束通话
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function endCall(array $params): bool
|
||||
{
|
||||
try {
|
||||
// 获取当前管理员ID(从参数中获取)
|
||||
$adminId = $params['admin_id'] ?? 0;
|
||||
|
||||
if (!$adminId) {
|
||||
self::setError('获取管理员信息失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查找最近的通话记录
|
||||
$record = \app\common\model\tcm\CallRecord::where('diagnosis_id', $params['diagnosis_id'])
|
||||
->where('caller_id', $adminId)
|
||||
->where('status', 1)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
|
||||
if ($record) {
|
||||
$endTime = time();
|
||||
$duration = $endTime - $record->start_time;
|
||||
|
||||
$record->save([
|
||||
'status' => 2, // 2-已结束
|
||||
'end_time' => $endTime,
|
||||
'duration' => $duration
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话记录
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getCallRecords(array $params): array
|
||||
{
|
||||
try {
|
||||
$records = \app\common\model\tcm\CallRecord::where('diagnosis_id', $params['diagnosis_id'])
|
||||
->order('id', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($records as &$record) {
|
||||
$record['start_time_text'] = date('Y-m-d H:i:s', $record['start_time']);
|
||||
$record['end_time_text'] = $record['end_time'] ? date('Y-m-d H:i:s', $record['end_time']) : '';
|
||||
$record['duration_text'] = self::formatDuration($record['duration']);
|
||||
}
|
||||
|
||||
return $records;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取TRTC配置
|
||||
* @return array|null
|
||||
*/
|
||||
private static function getTrtcConfig(): ?array
|
||||
{
|
||||
// 从配置文件或数据库读取
|
||||
// 这里使用配置文件方式
|
||||
$config = config('project.trtc');
|
||||
|
||||
if (empty($config['sdkAppId']) || empty($config['secretKey'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成UserSig
|
||||
* @param int $sdkAppId
|
||||
* @param string $secretKey
|
||||
* @param string $userId
|
||||
* @param int $expire
|
||||
* @return string|false
|
||||
*/
|
||||
private static function generateUserSig(int $sdkAppId, string $secretKey, string $userId, int $expire = 86400)
|
||||
{
|
||||
try {
|
||||
// 使用官方的TLSSigAPIv2类生成UserSig
|
||||
$api = new \app\common\service\TLSSigAPIv2($sdkAppId, $secretKey);
|
||||
$userSig = $api->genUserSig($userId, $expire);
|
||||
|
||||
return $userSig;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('生成UserSig失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 格式化通话时长
|
||||
* @param int $seconds
|
||||
* @return string
|
||||
*/
|
||||
private static function formatDuration(int $seconds): string
|
||||
{
|
||||
if ($seconds < 60) {
|
||||
return $seconds . '秒';
|
||||
} elseif ($seconds < 3600) {
|
||||
$minutes = floor($seconds / 60);
|
||||
$secs = $seconds % 60;
|
||||
return $minutes . '分' . $secs . '秒';
|
||||
} else {
|
||||
$hours = floor($seconds / 3600);
|
||||
$minutes = floor(($seconds % 3600) / 60);
|
||||
$secs = $seconds % 60;
|
||||
return $hours . '小时' . $minutes . '分' . $secs . '秒';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @notes 为患者创建 TRTC 账号(自动调用)
|
||||
* @param int $patientId
|
||||
* @return bool
|
||||
*/
|
||||
private static function createPatientTrtcAccount(int $patientId): bool
|
||||
{
|
||||
try {
|
||||
$config = self::getTrtcConfig();
|
||||
|
||||
if (!$config || !$config['enable']) {
|
||||
// 如果 TRTC 未启用,不创建账号
|
||||
return false;
|
||||
}
|
||||
|
||||
$userId = 'patient_' . $patientId;
|
||||
|
||||
// 检查是否已存在
|
||||
$existingAccount = \app\common\model\tcm\PatientTrtc::where('patient_id', $patientId)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
|
||||
// 生成 UserSig(长期有效,180天)
|
||||
$expireTime = 86400 * 180;
|
||||
$userSig = self::generateUserSig($config['sdkAppId'], $config['secretKey'], $userId, $expireTime);
|
||||
|
||||
if (!$userSig) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'patient_id' => $patientId,
|
||||
'user_id' => $userId,
|
||||
'user_sig' => $userSig,
|
||||
'sdk_app_id' => $config['sdkAppId'],
|
||||
'expire_time' => time() + $expireTime,
|
||||
'is_active' => 1
|
||||
];
|
||||
|
||||
if ($existingAccount) {
|
||||
// 更新现有账号
|
||||
$existingAccount->save($data);
|
||||
\think\facade\Log::info('更新患者 TRTC 账号 - patient_id: ' . $patientId . ', user_id: ' . $userId);
|
||||
} else {
|
||||
// 创建新账号
|
||||
\app\common\model\tcm\PatientTrtc::create($data);
|
||||
\think\facade\Log::info('创建患者 TRTC 账号 - patient_id: ' . $patientId . ', user_id: ' . $userId);
|
||||
}
|
||||
|
||||
// 导入账号到腾讯云IM(用于TUICallKit)
|
||||
self::importAccountToTencentIm($patientId, $userId);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('创建患者 TRTC 账号失败 - patient_id: ' . $patientId . ', error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 导入账号到腾讯云IM
|
||||
* @param int $patientId
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
*/
|
||||
private static function importAccountToTencentIm(int $patientId, string $userId): bool
|
||||
{
|
||||
try {
|
||||
// 获取患者信息
|
||||
$diagnosis = Diagnosis::where('patient_id', $patientId)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
|
||||
$nick = $diagnosis ? $diagnosis->patient_name : '患者' . $patientId;
|
||||
|
||||
// 调用IM服务导入账号
|
||||
$imService = new \app\common\service\TencentImService();
|
||||
$result = $imService->importAccount($userId, $nick);
|
||||
|
||||
if ($result['success']) {
|
||||
\think\facade\Log::info('导入患者IM账号成功 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', nick: ' . $nick);
|
||||
return true;
|
||||
} else {
|
||||
\think\facade\Log::warning('导入患者IM账号失败 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', error: ' . $result['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('导入患者IM账号异常 - patient_id: ' . $patientId . ', user_id: ' . $userId . ', error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者签名(供小程序调用)
|
||||
* @param int $patientId
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function getPatientSignature(int $patientId)
|
||||
{
|
||||
try {
|
||||
// 获取配置
|
||||
$config = self::getTrtcConfig();
|
||||
|
||||
if (!$config) {
|
||||
self::setError('请先配置腾讯云TRTC参数');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 患者userId
|
||||
$patientUserId = 'patient_' . $patientId;
|
||||
|
||||
// 生成患者的 UserSig
|
||||
$userSig = self::generateUserSig(
|
||||
$config['sdkAppId'],
|
||||
$config['secretKey'],
|
||||
$patientUserId
|
||||
);
|
||||
|
||||
if (!$userSig) {
|
||||
self::setError('生成签名失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 确保患者账号已导入IM
|
||||
self::ensurePatientImAccount($patientId, $patientUserId);
|
||||
|
||||
\think\facade\Log::info('小程序获取患者签名成功 - patient_id: ' . $patientId . ', user_id: ' . $patientUserId);
|
||||
|
||||
return [
|
||||
'sdkAppId' => (int)$config['sdkAppId'],
|
||||
'userId' => $patientUserId,
|
||||
'userSig' => $userSig,
|
||||
'expireTime' => 86400
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('获取患者签名失败 - patient_id: ' . $patientId . ', error: ' . $e->getMessage());
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医助列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getAssistants()
|
||||
{
|
||||
try {
|
||||
// 通过中间表查询角色ID为2的管理员(医助)
|
||||
$assistants = \app\common\model\auth\Admin::alias('a')
|
||||
->join('admin_role ar', 'a.id = ar.admin_id')
|
||||
->where('ar.role_id', 2)
|
||||
->where('a.disable', 0)
|
||||
->field(['a.id', 'a.name', 'a.account'])
|
||||
->order('a.id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $assistants;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('获取医助列表失败: ' . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getDoctors()
|
||||
{
|
||||
try {
|
||||
// 通过中间表查询角色ID为1的管理员(医生)
|
||||
$doctors = \app\common\model\auth\Admin::alias('a')
|
||||
->join('admin_role ar', 'a.id = ar.admin_id')
|
||||
->where('ar.role_id', 1)
|
||||
->where('a.disable', 0)
|
||||
->field(['a.id', 'a.name', 'a.account'])
|
||||
->order('a.id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $doctors;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('获取医生列表失败: ' . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\validate\doctor;
|
||||
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 医生预约验证器
|
||||
* Class AppointmentValidate
|
||||
* @package app\adminapi\validate\doctor
|
||||
*/
|
||||
class AppointmentValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 设置校验规则
|
||||
* @var string[]
|
||||
*/
|
||||
protected $rule = [
|
||||
'id' => 'require',
|
||||
'patient_id' => 'require|integer',
|
||||
'doctor_id' => 'require|integer',
|
||||
'appointment_date' => 'require|date',
|
||||
'period' => 'in:morning,afternoon,all',
|
||||
'appointment_time' => 'require',
|
||||
'appointment_type' => 'in:video,text,phone',
|
||||
];
|
||||
|
||||
/**
|
||||
* 参数描述
|
||||
* @var string[]
|
||||
*/
|
||||
protected $field = [
|
||||
'id' => '预约ID',
|
||||
'patient_id' => '患者ID',
|
||||
'doctor_id' => '医生ID',
|
||||
'appointment_date' => '预约日期',
|
||||
'period' => '时段',
|
||||
'appointment_time' => '预约时间',
|
||||
'appointment_type' => '预约类型',
|
||||
];
|
||||
|
||||
/**
|
||||
* @notes 创建预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneCreate()
|
||||
{
|
||||
return $this->only(['patient_id', 'doctor_id', 'appointment_date', 'period', 'appointment_time', 'appointment_type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 取消预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneCancel()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约详情场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneDetail()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 可用时间段场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneAvailableSlots()
|
||||
{
|
||||
return $this->only(['doctor_id', 'appointment_date', 'period']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 完成预约场景
|
||||
* @return AppointmentValidate
|
||||
*/
|
||||
public function sceneComplete()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace app\adminapi\validate\doctor;
|
||||
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 医生排班验证器
|
||||
* Class RosterValidate
|
||||
* @package app\adminapi\validate\doctor
|
||||
*/
|
||||
class RosterValidate extends BaseValidate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require',
|
||||
'doctor_id' => 'require',
|
||||
'date' => 'require|date',
|
||||
'period' => 'require|in:morning,afternoon',
|
||||
'status' => 'require|in:1,2,3,4',
|
||||
'quota' => 'number',
|
||||
'max_patients' => 'number',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'id.require' => '请选择排班',
|
||||
'doctor_id.require' => '请选择医生',
|
||||
'date.require' => '请选择日期',
|
||||
'date.date' => '日期格式不正确',
|
||||
'period.require' => '请选择时段',
|
||||
'period.in' => '时段参数错误',
|
||||
'status.require' => '请选择状态',
|
||||
'status.in' => '状态参数错误',
|
||||
'quota.number' => '号源数必须是数字',
|
||||
'max_patients.number' => '最大接诊数必须是数字',
|
||||
];
|
||||
|
||||
/**
|
||||
* @notes 保存场景
|
||||
*/
|
||||
public function sceneSave()
|
||||
{
|
||||
return $this->only(['doctor_id', 'date', 'period', 'status', 'quota', 'max_patients', 'remark']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除场景
|
||||
*/
|
||||
public function sceneDelete()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 详情场景
|
||||
*/
|
||||
public function sceneDetail()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量保存场景
|
||||
*/
|
||||
public function sceneBatchSave()
|
||||
{
|
||||
return $this->only(['rosters'])
|
||||
->append('rosters', 'require|array');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 复制场景
|
||||
*/
|
||||
public function sceneCopy()
|
||||
{
|
||||
return $this->only(['source_start_date', 'source_end_date', 'target_start_date'])
|
||||
->append('source_start_date', 'require|date')
|
||||
->append('source_end_date', 'require|date')
|
||||
->append('target_start_date', 'require|date');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\adminapi\validate\tcm;
|
||||
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\validate\BaseValidate;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单验证
|
||||
* Class DiagnosisValidate
|
||||
* @package app\adminapi\validate\tcm
|
||||
*/
|
||||
class DiagnosisValidate extends BaseValidate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require|checkDiagnosis',
|
||||
'patient_name' => 'require|length:1,50',
|
||||
'id_card' => 'length:15,18',
|
||||
'phone' => 'require|mobile',
|
||||
'gender' => 'require|in:0,1',
|
||||
'age' => 'require|number|between:0,150',
|
||||
'diagnosis_date' => 'require',
|
||||
'diagnosis_type' => 'require',
|
||||
'syndrome_type' => 'require',
|
||||
'status' => 'in:0,1',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'id.require' => '参数缺失',
|
||||
'patient_name.require' => '请输入患者姓名',
|
||||
'patient_name.length' => '患者姓名长度须在1-50位字符',
|
||||
'id_card.length' => '身份证号长度不正确',
|
||||
'phone.require' => '请输入手机号',
|
||||
'phone.mobile' => '手机号格式不正确',
|
||||
'gender.require' => '请选择性别',
|
||||
'gender.in' => '性别参数错误',
|
||||
'age.require' => '请输入年龄',
|
||||
'age.number' => '年龄必须为数字',
|
||||
'age.between' => '年龄范围0-150',
|
||||
'diagnosis_date.require' => '请选择诊断日期',
|
||||
'diagnosis_type.require' => '请选择诊断类型',
|
||||
'syndrome_type.require' => '请选择证型',
|
||||
'status.in' => '状态参数错误',
|
||||
];
|
||||
|
||||
public function sceneAdd()
|
||||
{
|
||||
return $this->remove('id', true);
|
||||
}
|
||||
|
||||
public function sceneEdit()
|
||||
{
|
||||
return $this->only(['id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'past_history', 'symptoms', 'tongue_coating', 'pulse', 'treatment_principle', 'prescription', 'doctor_advice', 'remark', 'status']);
|
||||
}
|
||||
|
||||
public function sceneId()
|
||||
{
|
||||
return $this->only(['id']);
|
||||
}
|
||||
|
||||
protected function checkDiagnosis($value)
|
||||
{
|
||||
$diagnosis = Diagnosis::findOrEmpty($value);
|
||||
if ($diagnosis->isEmpty()) {
|
||||
return '诊单不存在';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
|
||||
/**
|
||||
* 中医诊断控制器(供小程序调用)
|
||||
* Class TcmController
|
||||
* @package app\api\controller
|
||||
*/
|
||||
class TcmController extends BaseApiController
|
||||
{
|
||||
/**
|
||||
* @notes 不需要登录的方法
|
||||
* @var array
|
||||
*/
|
||||
public array $notNeedLogin = ['getPatientSignature'];
|
||||
|
||||
/**
|
||||
* @notes 获取患者签名(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getPatientSignature()
|
||||
{
|
||||
$patientId = $this->request->get('patient_id');
|
||||
|
||||
if (!$patientId) {
|
||||
return $this->fail('患者ID不能为空');
|
||||
}
|
||||
|
||||
// 调用逻辑层
|
||||
$result = DiagnosisLogic::getPatientSignature($patientId);
|
||||
|
||||
if ($result === false) {
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
|
||||
return $this->data($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\doctor;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 医生预约模型
|
||||
* Class Appointment
|
||||
* @package app\common\model\doctor
|
||||
*/
|
||||
class Appointment extends BaseModel
|
||||
{
|
||||
protected $name = 'doctor_appointment';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
1 => '已预约',
|
||||
2 => '已取消',
|
||||
3 => '已完成',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 时段描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getPeriodDescAttr($value, $data)
|
||||
{
|
||||
return $data['period'] == 'morning' ? '上午' : '下午';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约类型描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getAppointmentTypeDescAttr($value, $data)
|
||||
{
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
return $typeMap[$data['appointment_type']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 预约日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getAppointmentDateTextAttr($value, $data)
|
||||
{
|
||||
return $data['appointment_date'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 关联患者
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function patient()
|
||||
{
|
||||
return $this->hasOne('app\common\model\user\User', 'id', 'patient_id')
|
||||
->bind(['patient_name' => 'nickname', 'patient_phone' => 'mobile']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 关联医生
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function doctor()
|
||||
{
|
||||
return $this->hasOne('app\adminapi\logic\auth\AdminLogic', 'id', 'doctor_id')
|
||||
->bind(['doctor_name' => 'name']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\doctor;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 医生排班模型
|
||||
* Class Roster
|
||||
* @package app\common\model\doctor
|
||||
*/
|
||||
class Roster extends BaseModel
|
||||
{
|
||||
protected $name = 'doctor_roster';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
1 => '出诊',
|
||||
2 => '停诊',
|
||||
3 => '休息',
|
||||
4 => '请假',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 时段描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getPeriodDescAttr($value, $data)
|
||||
{
|
||||
return $data['period'] == 'morning' ? '上午' : '下午';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTextAttr($value, $data)
|
||||
{
|
||||
return $data['date'] ?? '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 血糖血压记录模型
|
||||
* Class BloodRecord
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class BloodRecord extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_blood_record';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 记录日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getRecordDateTextAttr($value, $data)
|
||||
{
|
||||
return !empty($data['record_date']) ? date('Y-m-d', $data['record_date']) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 血压文本
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getBloodPressureTextAttr($value, $data)
|
||||
{
|
||||
if (!empty($data['systolic_pressure']) && !empty($data['diastolic_pressure'])) {
|
||||
return $data['systolic_pressure'] . '/' . $data['diastolic_pressure'] . ' mmHg';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 通话记录模型
|
||||
* Class CallRecord
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class CallRecord extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_call_record';
|
||||
|
||||
/**
|
||||
* @notes 关联诊单
|
||||
*/
|
||||
public function diagnosis()
|
||||
{
|
||||
return $this->belongsTo('app\common\model\tcm\Diagnosis', 'diagnosis_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取通话类型文本
|
||||
*/
|
||||
public function getCallTypeTextAttr($value, $data)
|
||||
{
|
||||
$types = [
|
||||
1 => '语音通话',
|
||||
2 => '视频通话'
|
||||
];
|
||||
return $types[$data['call_type']] ?? '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取状态文本
|
||||
*/
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$status = [
|
||||
1 => '进行中',
|
||||
2 => '已结束',
|
||||
3 => '未接听',
|
||||
4 => '已取消'
|
||||
];
|
||||
return $status[$data['status']] ?? '未知';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
||||
// +----------------------------------------------------------------------
|
||||
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
||||
// | 开源版本可自由商用,可去除界面版权logo
|
||||
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
||||
// | github下载:https://github.com/likeshop-github/likeadmin
|
||||
// | 访问官网:https://www.likeadmin.cn
|
||||
// | likeadmin团队 版权所有 拥有最终解释权
|
||||
// +----------------------------------------------------------------------
|
||||
// | author: likeadminTeam
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 中医辨房病因诊单模型
|
||||
* Class Diagnosis
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class Diagnosis extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_diagnosis';
|
||||
|
||||
// 自动时间戳类型设置为整型
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
// 时间字段取出后的默认时间格式(设置为false表示保持整型)
|
||||
protected $dateFormat = false;
|
||||
|
||||
/**
|
||||
* @notes 性别描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getGenderDescAttr($value, $data)
|
||||
{
|
||||
return $data['gender'] == 1 ? '男' : '女';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 状态描述
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusDescAttr($value, $data)
|
||||
{
|
||||
return $data['status'] == 1 ? '启用' : '禁用';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 诊断日期格式化
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function getDiagnosisDateTextAttr($value, $data)
|
||||
{
|
||||
return !empty($data['diagnosis_date']) ? date('Y-m-d', $data['diagnosis_date']) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 既往史数组
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function getPastHistoryArrAttr($value, $data)
|
||||
{
|
||||
return !empty($data['past_history']) ? explode(',', $data['past_history']) : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 患者TRTC账号模型
|
||||
* Class PatientTrtc
|
||||
* @package app\common\model\tcm
|
||||
*/
|
||||
class PatientTrtc extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_patient_trtc';
|
||||
|
||||
/**
|
||||
* @notes 关联诊单
|
||||
*/
|
||||
public function diagnosis()
|
||||
{
|
||||
return $this->hasMany('app\common\model\tcm\Diagnosis', 'patient_id', 'patient_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 检查是否过期
|
||||
*/
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return $this->expire_time > 0 && $this->expire_time < time();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 更新最后登录时间
|
||||
*/
|
||||
public function updateLastLogin(): bool
|
||||
{
|
||||
$this->last_login_time = time();
|
||||
return $this->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
/**
|
||||
* 腾讯云IM UserSig生成类(官方算法)
|
||||
* 参考:https://github.com/tencentyun/tls-sig-api-v2-php
|
||||
*/
|
||||
class TLSSigAPIv2
|
||||
{
|
||||
private $sdkappid;
|
||||
private $key;
|
||||
|
||||
public function __construct($sdkappid, $key)
|
||||
{
|
||||
$this->sdkappid = $sdkappid;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $expire 过期时间(秒)
|
||||
* @return string UserSig
|
||||
*/
|
||||
public function genUserSig($identifier, $expire = 86400)
|
||||
{
|
||||
return $this->__genSig($identifier, $expire, '', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带权限的UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $expire 过期时间(秒)
|
||||
* @param string $userbuf 用户权限buffer
|
||||
* @return string UserSig
|
||||
*/
|
||||
public function genUserSigWithUserBuf($identifier, $expire, $userbuf)
|
||||
{
|
||||
return $this->__genSig($identifier, $expire, $userbuf, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部方法:生成签名
|
||||
*/
|
||||
private function __genSig($identifier, $expire, $userbuf, $userbuf_enabled)
|
||||
{
|
||||
$current = time();
|
||||
$sigDoc = [
|
||||
'TLS.ver' => '2.0',
|
||||
'TLS.identifier' => strval($identifier),
|
||||
'TLS.sdkappid' => intval($this->sdkappid),
|
||||
'TLS.expire' => intval($expire),
|
||||
'TLS.time' => intval($current)
|
||||
];
|
||||
|
||||
$base64_userbuf = '';
|
||||
if ($userbuf_enabled) {
|
||||
$base64_userbuf = base64_encode($userbuf);
|
||||
$sigDoc['TLS.userbuf'] = $base64_userbuf;
|
||||
}
|
||||
|
||||
$sig = $this->hmacsha256($identifier, $current, $expire, $base64_userbuf, $userbuf_enabled);
|
||||
$sigDoc['TLS.sig'] = base64_encode($sig);
|
||||
|
||||
$json_text = json_encode($sigDoc);
|
||||
$compressed = gzcompress($json_text);
|
||||
|
||||
return $this->base64_url_encode($compressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成HMAC-SHA256签名
|
||||
*/
|
||||
private function hmacsha256($identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled)
|
||||
{
|
||||
$content_to_be_signed = "TLS.identifier:" . $identifier . "\n"
|
||||
. "TLS.sdkappid:" . $this->sdkappid . "\n"
|
||||
. "TLS.time:" . $curr_time . "\n"
|
||||
. "TLS.expire:" . $expire . "\n";
|
||||
|
||||
if ($userbuf_enabled) {
|
||||
$content_to_be_signed .= "TLS.userbuf:" . $base64_userbuf . "\n";
|
||||
}
|
||||
|
||||
return hash_hmac('sha256', $content_to_be_signed, $this->key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 URL安全编码
|
||||
*/
|
||||
private function base64_url_encode($input)
|
||||
{
|
||||
return str_replace(['+', '/', '='], ['*', '-', '_'], base64_encode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 URL安全解码
|
||||
*/
|
||||
private function base64_url_decode($base64_url_string)
|
||||
{
|
||||
return base64_decode(str_replace(['*', '-', '_'], ['+', '/', '='], $base64_url_string));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证UserSig
|
||||
* @param string $sig UserSig
|
||||
* @param string $identifier 用户ID
|
||||
* @param int $init_time 初始化时间
|
||||
* @param int $expire_time 过期时间
|
||||
* @param string $userbuf 用户权限buffer
|
||||
* @param string $error_msg 错误信息
|
||||
* @return bool 是否有效
|
||||
*/
|
||||
public function verifySig($sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg)
|
||||
{
|
||||
try {
|
||||
$error_msg = '';
|
||||
$compressed_sig = $this->base64_url_decode($sig);
|
||||
$pre_level = error_reporting(E_ERROR);
|
||||
$uncompressed_sig = gzuncompress($compressed_sig);
|
||||
error_reporting($pre_level);
|
||||
|
||||
if ($uncompressed_sig === false) {
|
||||
throw new \Exception('gzuncompress error');
|
||||
}
|
||||
|
||||
$sig_doc = json_decode($uncompressed_sig, true);
|
||||
if ($sig_doc === false) {
|
||||
throw new \Exception('json_decode error');
|
||||
}
|
||||
|
||||
if ($sig_doc['TLS.identifier'] !== $identifier) {
|
||||
throw new \Exception('identifier dosen\'t match');
|
||||
}
|
||||
|
||||
if ($sig_doc['TLS.sdkappid'] != $this->sdkappid) {
|
||||
throw new \Exception('sdkappid dosen\'t match');
|
||||
}
|
||||
|
||||
$sig = base64_decode($sig_doc['TLS.sig']);
|
||||
if ($sig === false) {
|
||||
throw new \Exception('sig base64_decode error');
|
||||
}
|
||||
|
||||
$init_time = $sig_doc['TLS.time'];
|
||||
$expire_time = $sig_doc['TLS.expire'];
|
||||
|
||||
$curr_time = time();
|
||||
if ($curr_time > $init_time + $expire_time) {
|
||||
throw new \Exception('sig expired');
|
||||
}
|
||||
|
||||
$userbuf_enabled = false;
|
||||
$base64_userbuf = '';
|
||||
if (isset($sig_doc['TLS.userbuf'])) {
|
||||
$base64_userbuf = $sig_doc['TLS.userbuf'];
|
||||
$userbuf = base64_decode($base64_userbuf);
|
||||
$userbuf_enabled = true;
|
||||
}
|
||||
|
||||
$sigCalculated = $this->hmacsha256($identifier, $init_time, $expire_time, $base64_userbuf, $userbuf_enabled);
|
||||
|
||||
if ($sig != $sigCalculated) {
|
||||
throw new \Exception('verify failed');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $ex) {
|
||||
$error_msg = $ex->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
/**
|
||||
* 腾讯云IM服务类
|
||||
* Class TencentImService
|
||||
* @package app\common\service
|
||||
*/
|
||||
class TencentImService
|
||||
{
|
||||
private $sdkAppId;
|
||||
private $secretKey;
|
||||
private $adminIdentifier = 'administrator'; // 管理员账号
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = config('project.trtc');
|
||||
$this->sdkAppId = $config['sdkAppId'];
|
||||
$this->secretKey = $config['secretKey'];
|
||||
|
||||
\think\facade\Log::info('TencentImService初始化 - sdkAppId: ' . $this->sdkAppId . ', secretKey长度: ' . strlen($this->secretKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成UserSig(使用腾讯云官方算法)
|
||||
* @param string $userId
|
||||
* @param int $expire
|
||||
* @return string|false
|
||||
*/
|
||||
private function generateUserSig(string $userId, int $expire = 86400)
|
||||
{
|
||||
try {
|
||||
$api = new TLSSigAPIv2($this->sdkAppId, $this->secretKey);
|
||||
$userSig = $api->genUserSig($userId, $expire);
|
||||
|
||||
\think\facade\Log::info('UserSig生成成功 - userId: ' . $userId . ', sig长度: ' . strlen($userSig));
|
||||
|
||||
return $userSig;
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('生成UserSig失败: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 导入单个账号到IM
|
||||
* @param string $userId 用户ID
|
||||
* @param string $nick 昵称(可选)
|
||||
* @param string $faceUrl 头像URL(可选)
|
||||
* @return array|false
|
||||
*/
|
||||
public function importAccount(string $userId, string $nick = '', string $faceUrl = '')
|
||||
{
|
||||
try {
|
||||
\think\facade\Log::info('开始导入IM账号 - userId: ' . $userId . ', nick: ' . $nick . ', sdkAppId: ' . $this->sdkAppId);
|
||||
|
||||
// 生成管理员UserSig
|
||||
$adminUserSig = $this->generateUserSig($this->adminIdentifier);
|
||||
|
||||
if (!$adminUserSig) {
|
||||
throw new \Exception('生成管理员UserSig失败');
|
||||
}
|
||||
|
||||
\think\facade\Log::info('管理员UserSig生成成功');
|
||||
|
||||
// 构建请求URL
|
||||
$random = rand(0, 4294967295);
|
||||
$url = sprintf(
|
||||
'https://console.tim.qq.com/v4/im_open_login_svc/account_import?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json',
|
||||
$this->sdkAppId,
|
||||
$this->adminIdentifier,
|
||||
urlencode($adminUserSig),
|
||||
$random
|
||||
);
|
||||
|
||||
\think\facade\Log::info('请求URL构建完成');
|
||||
|
||||
// 构建请求体
|
||||
$data = [
|
||||
'UserID' => $userId
|
||||
];
|
||||
|
||||
if ($nick) {
|
||||
$data['Nick'] = $nick;
|
||||
}
|
||||
|
||||
if ($faceUrl) {
|
||||
$data['FaceUrl'] = $faceUrl;
|
||||
}
|
||||
|
||||
\think\facade\Log::info('请求数据: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 发送请求
|
||||
$result = $this->httpPost($url, json_encode($data));
|
||||
|
||||
\think\facade\Log::info('HTTP响应: ' . substr($result, 0, 500));
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('请求失败:无响应');
|
||||
}
|
||||
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if (!$response) {
|
||||
throw new \Exception('响应解析失败:' . substr($result, 0, 200));
|
||||
}
|
||||
|
||||
\think\facade\Log::info('响应解析成功: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 检查响应状态
|
||||
if ($response['ActionStatus'] !== 'OK') {
|
||||
$errorMsg = sprintf(
|
||||
'导入账号失败 - ErrorCode: %s, ErrorInfo: %s',
|
||||
$response['ErrorCode'] ?? 'unknown',
|
||||
$response['ErrorInfo'] ?? '未知错误'
|
||||
);
|
||||
throw new \Exception($errorMsg);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'userId' => $userId,
|
||||
'message' => '账号导入成功'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$errorMsg = sprintf(
|
||||
'导入IM账号失败 - userId: %s, error: %s',
|
||||
$userId,
|
||||
$e->getMessage()
|
||||
);
|
||||
\think\facade\Log::error($errorMsg);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'userId' => $userId,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 批量导入账号
|
||||
* @param array $accounts 账号列表 [['userId' => 'xxx', 'nick' => 'xxx', 'faceUrl' => 'xxx'], ...]
|
||||
* @return array
|
||||
*/
|
||||
public function batchImportAccounts(array $accounts): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$userId = $account['userId'] ?? '';
|
||||
$nick = $account['nick'] ?? '';
|
||||
$faceUrl = $account['faceUrl'] ?? '';
|
||||
|
||||
if (!$userId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->importAccount($userId, $nick, $faceUrl);
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 删除账号
|
||||
* @param array $userIds 用户ID列表
|
||||
* @return array|false
|
||||
*/
|
||||
public function deleteAccounts(array $userIds)
|
||||
{
|
||||
try {
|
||||
// 生成管理员UserSig
|
||||
$adminUserSig = $this->generateUserSig($this->adminIdentifier);
|
||||
|
||||
if (!$adminUserSig) {
|
||||
throw new \Exception('生成管理员UserSig失败');
|
||||
}
|
||||
|
||||
// 构建请求URL
|
||||
$random = rand(0, 4294967295);
|
||||
$url = sprintf(
|
||||
'https://console.tim.qq.com/v4/im_open_login_svc/account_delete?sdkappid=%s&identifier=%s&usersig=%s&random=%s&contenttype=json',
|
||||
$this->sdkAppId,
|
||||
$this->adminIdentifier,
|
||||
urlencode($adminUserSig),
|
||||
$random
|
||||
);
|
||||
|
||||
// 构建请求体
|
||||
$data = [
|
||||
'DeleteItem' => array_map(function($userId) {
|
||||
return ['UserID' => $userId];
|
||||
}, $userIds)
|
||||
];
|
||||
|
||||
// 发送请求
|
||||
$result = $this->httpPost($url, json_encode($data));
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('请求失败');
|
||||
}
|
||||
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if (!$response) {
|
||||
throw new \Exception('响应解析失败');
|
||||
}
|
||||
|
||||
// 检查响应状态
|
||||
if ($response['ActionStatus'] !== 'OK') {
|
||||
throw new \Exception($response['ErrorInfo'] ?? '删除账号失败');
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => '账号删除成功'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('删除IM账号失败', [
|
||||
'userIds' => $userIds,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 发送HTTP POST请求
|
||||
* @param string $url
|
||||
* @param string $data
|
||||
* @return string|false
|
||||
*/
|
||||
private function httpPost(string $url, string $data)
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
]);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$error = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
\think\facade\Log::error('HTTP请求失败', [
|
||||
'url' => $url,
|
||||
'error' => $error
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class IndexController extends BaseController
|
||||
*/
|
||||
public function index($name = '你好,likeadmin')
|
||||
{
|
||||
$template = app()->getRootPath() . 'public/pc/index.html';
|
||||
$template = app()->getRootPath() . 'public/admin/index.html';
|
||||
if (Request::isMobile()) {
|
||||
$template = app()->getRootPath() . 'public/mobile/index.html';
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ return [
|
||||
// 错误显示信息,非调试模式有效
|
||||
'error_message' => '页面错误!请稍后再试~',
|
||||
// 显示错误信息
|
||||
'show_error_msg' => false,
|
||||
'show_error_msg' => true,
|
||||
];
|
||||
|
||||
@@ -100,6 +100,14 @@ return [
|
||||
'decorate' => [
|
||||
// 底部导航栏样式设置
|
||||
'tabbar_style' => ['default_color' => '#999999', 'selected_color' => '#c455ff'],
|
||||
],
|
||||
|
||||
// 腾讯云实时音视频(TRTC)配置
|
||||
'trtc' => [
|
||||
'sdkAppId' => (int)env('trtc.sdk_app_id', 1600127710),
|
||||
'secretKey' => env('trtc.secret_key', '8a6b3ce533b0e46b9d6e17d5c77bac240bc0ccdce4e3db93a0d6a1e55160c73d'),
|
||||
'expireTime' => 86400,
|
||||
'enable' => env('trtc.enable', false),
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* 腾讯云实时音视频(TRTC)配置
|
||||
*/
|
||||
return [
|
||||
// SDK AppID - 在腾讯云控制台获取
|
||||
'sdkAppId' => env('trtc.sdk_app_id', 0),
|
||||
|
||||
// 密钥 - 在腾讯云控制台获取
|
||||
'secretKey' => env('trtc.secret_key', ''),
|
||||
|
||||
// UserSig过期时间(秒)默认24小时
|
||||
'expireTime' => 86400,
|
||||
|
||||
// 是否启用
|
||||
'enable' => env('trtc.enable', false),
|
||||
];
|
||||
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks -Multiviews
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
|
||||
</IfModule>
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
import r from"./error-CRtkD2zz.js";import{d as p,c as i,o as m,l as e,s,a as o}from"./@vue-DqCYILjk.js";import"./element-plus-Caccz9g_.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-B-0K5V-K.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";const a="/admin/assets/no_perms-jDxcYpYC.png",n={class:"error404"},P=p({__name:"403",setup(c){return(l,t)=>(m(),i("div",n,[e(r,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:s(()=>t[0]||(t[0]=[o("div",{class:"flex justify-center"},[o("img",{class:"w-[150px] h-[150px]",src:a,alt:""})],-1)])),_:1})]))}});export{P as default};
|
||||
@@ -1 +0,0 @@
|
||||
import t from"./error-kreLlkAh.js";import{d as o,o as r,a,m as n,w as c,b as s}from"./index-B2xNDy79.js";const p="/admin/assets/no_perms-jDxcYpYC.png",i={class:"error404"},f=o({__name:"403",setup(m){return(_,e)=>(r(),a("div",i,[n(t,{code:"403",title:"您的账号权限不足,请联系管理员添加权限!","show-btn":!1},{content:c(()=>e[0]||(e[0]=[s("div",{class:"flex justify-center"},[s("img",{class:"w-[150px] h-[150px]",src:p,alt:""})],-1)])),_:1})]))}});export{f as default};
|
||||
@@ -1 +0,0 @@
|
||||
import e from"./error-kreLlkAh.js";import{d as o,o as r,a as t,m as a}from"./index-B2xNDy79.js";const s={class:"error404"},d=o({__name:"404",setup(c){return(n,_)=>(r(),t("div",s,[a(e,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{d as default};
|
||||
@@ -0,0 +1 @@
|
||||
import o from"./error-CRtkD2zz.js";import{d as r,c as t,o as m,l as p}from"./@vue-DqCYILjk.js";import"./element-plus-Caccz9g_.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./vue-router-B-0K5V-K.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";const i={class:"error404"},M=r({__name:"404",setup(e){return(c,s)=>(m(),t("div",i,[p(o,{code:"404",title:"哎呀,出错了!您访问的页面不存在…"})]))}});export{M as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import{H as u}from"./highlight.js-CCvalMLC.js";import{d as c,h as g,r as d,w as h,b as n}from"./@vue-DqCYILjk.js";var i=c({props:{code:{type:String,required:!0},language:{type:String,default:""},autodetect:{type:Boolean,default:!0},ignoreIllegals:{type:Boolean,default:!0}},setup:function(e){var t=d(e.language);h(function(){return e.language},function(a){t.value=a});var r=n(function(){return e.autodetect||!t.value}),o=n(function(){return!r.value&&!u.getLanguage(t.value)});return{className:n(function(){return o.value?"":"hljs "+t.value}),highlightedCode:n(function(){var a;if(o.value)return console.warn('The language "'+t.value+'" you specified could not be found.'),e.code.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");if(r.value){var l=u.highlightAuto(e.code);return t.value=(a=l.language)!==null&&a!==void 0?a:"",l.value}return(l=u.highlight(e.code,{language:t.value,ignoreIllegals:e.ignoreIllegals})).value})}},render:function(){return g("pre",{},[g("code",{class:this.className,innerHTML:this.highlightedCode})])}}),f={install:function(e){e.component("highlightjs",i)},component:i};export{f as o};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.blood-record-list[data-v-ff1e1156]{padding:20px}
|
||||
@@ -1 +0,0 @@
|
||||
import{aU as l,aV as i,aW as E,aX as L,aQ as D,aY as A,aZ as F,a_ as G,a$ as K,aS as U,b0 as m,b1 as y,b2 as W,aO as R,b3 as u}from"./index-B2xNDy79.js";var h=l(i,"WeakMap");function q(t){return t!=null&&E(t.length)&&!L(t)}var N=Object.prototype;function Y(t){var r=t&&t.constructor,e=typeof r=="function"&&r.prototype||N;return t===e}function Z(t,r){for(var e=-1,n=Array(t);++e<t;)n[e]=r(e);return n}function H(){return!1}var k=typeof exports=="object"&&exports&&!exports.nodeType&&exports,w=k&&typeof module=="object"&&module&&!module.nodeType&&module,Q=w&&w.exports===k,x=Q?i.Buffer:void 0,X=x?x.isBuffer:void 0,J=X||H,tt="[object Arguments]",et="[object Array]",rt="[object Boolean]",at="[object Date]",nt="[object Error]",ot="[object Function]",st="[object Map]",it="[object Number]",ut="[object Object]",ct="[object RegExp]",pt="[object Set]",ft="[object String]",gt="[object WeakMap]",bt="[object ArrayBuffer]",yt="[object DataView]",lt="[object Float32Array]",dt="[object Float64Array]",Tt="[object Int8Array]",ht="[object Int16Array]",jt="[object Int32Array]",vt="[object Uint8Array]",_t="[object Uint8ClampedArray]",At="[object Uint16Array]",mt="[object Uint32Array]",a={};a[lt]=a[dt]=a[Tt]=a[ht]=a[jt]=a[vt]=a[_t]=a[At]=a[mt]=!0;a[tt]=a[et]=a[bt]=a[rt]=a[yt]=a[at]=a[nt]=a[ot]=a[st]=a[it]=a[ut]=a[ct]=a[pt]=a[ft]=a[gt]=!1;function wt(t){return D(t)&&E(t.length)&&!!a[A(t)]}function xt(t){return function(r){return t(r)}}var z=typeof exports=="object"&&exports&&!exports.nodeType&&exports,g=z&&typeof module=="object"&&module&&!module.nodeType&&module,St=g&&g.exports===z,T=St&&F.process,S=function(){try{var t=g&&g.require&&g.require("util").types;return t||T&&T.binding&&T.binding("util")}catch{}}(),O=S&&S.isTypedArray,Ot=O?xt(O):wt,Pt=Object.prototype,$t=Pt.hasOwnProperty;function Mt(t,r){var e=U(t),n=!e&&G(t),c=!e&&!n&&J(t),p=!e&&!n&&!c&&Ot(t),f=e||n||c||p,d=f?Z(t.length,String):[],V=d.length;for(var o in t)(r||$t.call(t,o))&&!(f&&(o=="length"||c&&(o=="offset"||o=="parent")||p&&(o=="buffer"||o=="byteLength"||o=="byteOffset")||K(o,V)))&&d.push(o);return d}function Ct(t,r){return function(e){return t(r(e))}}var It=Ct(Object.keys,Object),Bt=Object.prototype,Et=Bt.hasOwnProperty;function Ut(t){if(!Y(t))return It(t);var r=[];for(var e in Object(t))Et.call(t,e)&&e!="constructor"&&r.push(e);return r}function kt(t){return q(t)?Mt(t):Ut(t)}function zt(){this.__data__=new m,this.size=0}function Vt(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e}function Lt(t){return this.__data__.get(t)}function Dt(t){return this.__data__.has(t)}var Ft=200;function Gt(t,r){var e=this.__data__;if(e instanceof m){var n=e.__data__;if(!y||n.length<Ft-1)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new W(n)}return e.set(t,r),this.size=e.size,this}function b(t){var r=this.__data__=new m(t);this.size=r.size}b.prototype.clear=zt;b.prototype.delete=Vt;b.prototype.get=Lt;b.prototype.has=Dt;b.prototype.set=Gt;function Kt(t,r){for(var e=-1,n=t==null?0:t.length,c=0,p=[];++e<n;){var f=t[e];r(f,e,t)&&(p[c++]=f)}return p}function Wt(){return[]}var Rt=Object.prototype,qt=Rt.propertyIsEnumerable,P=Object.getOwnPropertySymbols,Nt=P?function(t){return t==null?[]:(t=Object(t),Kt(P(t),function(r){return qt.call(t,r)}))}:Wt;function Yt(t,r,e){var n=r(t);return U(t)?n:R(n,e(t))}function re(t){return Yt(t,kt,Nt)}var j=l(i,"DataView"),v=l(i,"Promise"),_=l(i,"Set"),$="[object Map]",Zt="[object Object]",M="[object Promise]",C="[object Set]",I="[object WeakMap]",B="[object DataView]",Ht=u(j),Qt=u(y),Xt=u(v),Jt=u(_),te=u(h),s=A;(j&&s(new j(new ArrayBuffer(1)))!=B||y&&s(new y)!=$||v&&s(v.resolve())!=M||_&&s(new _)!=C||h&&s(new h)!=I)&&(s=function(t){var r=A(t),e=r==Zt?t.constructor:void 0,n=e?u(e):"";if(n)switch(n){case Ht:return B;case Qt:return $;case Xt:return M;case Jt:return C;case te:return I}return r});var ae=i.Uint8Array;export{b as S,ae as U,s as a,Yt as b,xt as c,re as d,Y as e,q as f,Nt as g,Mt as h,J as i,Ot as j,kt as k,S as n,Ct as o,Wt as s};
|
||||
@@ -1 +0,0 @@
|
||||
import{k as F,g as C,s as _,b as N,a as l,n as u,c as E,i as v,S as R,d as K}from"./_Uint8Array-0jgVjd-W.js";import{aO as Q,aP as $,aQ as B,aR as W,aS as Y,aT as q}from"./index-B2xNDy79.js";import{c as y,k as j,g as H,a as x,b as J,d as V,e as X,i as Z}from"./_initCloneObject-C-h6JGU9.js";function z(e,r){for(var n=-1,s=e==null?0:e.length;++n<s&&r(e[n],n,e)!==!1;);return e}function k(e,r){return e&&y(r,F(r),e)}function ee(e,r){return e&&y(r,j(r),e)}function re(e,r){return y(e,C(e),r)}var te=Object.getOwnPropertySymbols,L=te?function(e){for(var r=[];e;)Q(r,C(e)),e=H(e);return r}:_;function ne(e,r){return y(e,L(e),r)}function ae(e){return N(e,j,L)}var oe=Object.prototype,se=oe.hasOwnProperty;function ce(e){var r=e.length,n=new e.constructor(r);return r&&typeof e[0]=="string"&&se.call(e,"index")&&(n.index=e.index,n.input=e.input),n}function ie(e,r){var n=r?x(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}var be=/\w*$/;function ge(e){var r=new e.constructor(e.source,be.exec(e));return r.lastIndex=e.lastIndex,r}var O=$?$.prototype:void 0,w=O?O.valueOf:void 0;function fe(e){return w?Object(w.call(e)):{}}var ue="[object Boolean]",ye="[object Date]",Te="[object Map]",le="[object Number]",je="[object RegExp]",pe="[object Set]",Ae="[object String]",de="[object Symbol]",me="[object ArrayBuffer]",Se="[object DataView]",$e="[object Float32Array]",Oe="[object Float64Array]",we="[object Int8Array]",Ie="[object Int16Array]",he="[object Int32Array]",Fe="[object Uint8Array]",Ce="[object Uint8ClampedArray]",Ee="[object Uint16Array]",Be="[object Uint32Array]";function xe(e,r,n){var s=e.constructor;switch(r){case me:return x(e);case ue:case ye:return new s(+e);case Se:return ie(e,n);case $e:case Oe:case we:case Ie:case he:case Fe:case Ce:case Ee:case Be:return J(e,n);case Te:return new s;case le:case Ae:return new s(e);case je:return ge(e);case pe:return new s;case de:return fe(e)}}var Le="[object Map]";function Me(e){return B(e)&&l(e)==Le}var I=u&&u.isMap,Ue=I?E(I):Me,Pe="[object Set]";function De(e){return B(e)&&l(e)==Pe}var h=u&&u.isSet,Ge=h?E(h):De,_e=1,Ne=2,ve=4,M="[object Arguments]",Re="[object Array]",Ke="[object Boolean]",Qe="[object Date]",We="[object Error]",U="[object Function]",Ye="[object GeneratorFunction]",qe="[object Map]",He="[object Number]",P="[object Object]",Je="[object RegExp]",Ve="[object Set]",Xe="[object String]",Ze="[object Symbol]",ze="[object WeakMap]",ke="[object ArrayBuffer]",er="[object DataView]",rr="[object Float32Array]",tr="[object Float64Array]",nr="[object Int8Array]",ar="[object Int16Array]",or="[object Int32Array]",sr="[object Uint8Array]",cr="[object Uint8ClampedArray]",ir="[object Uint16Array]",br="[object Uint32Array]",t={};t[M]=t[Re]=t[ke]=t[er]=t[Ke]=t[Qe]=t[rr]=t[tr]=t[nr]=t[ar]=t[or]=t[qe]=t[He]=t[P]=t[Je]=t[Ve]=t[Xe]=t[Ze]=t[sr]=t[cr]=t[ir]=t[br]=!0;t[We]=t[U]=t[ze]=!1;function T(e,r,n,s,p,c){var a,g=r&_e,f=r&Ne,D=r&ve;if(a!==void 0)return a;if(!W(e))return e;var A=Y(e);if(A){if(a=ce(e),!g)return V(e,a)}else{var b=l(e),d=b==U||b==Ye;if(v(e))return X(e,g);if(b==P||b==M||d&&!p){if(a=f||d?{}:Z(e),!g)return f?ne(e,ee(a,e)):re(e,k(a,e))}else{if(!t[b])return p?e:{};a=xe(e,b,g)}}c||(c=new R);var m=c.get(e);if(m)return m;c.set(e,a),Ge(e)?e.forEach(function(o){a.add(T(o,r,n,o,e,c))}):Ue(e)&&e.forEach(function(o,i){a.set(i,T(o,r,n,i,e,c))});var G=D?f?ae:K:f?j:F,S=A?void 0:G(e);return z(S||e,function(o,i){S&&(i=o,o=e[i]),q(a,i,T(o,r,n,i,e,c))}),a}export{T as b};
|
||||
@@ -1 +0,0 @@
|
||||
import{aR as p,b4 as y,aT as O,aV as m}from"./index-B2xNDy79.js";import{e as v,f as x,h as w,o as P,U as a}from"./_Uint8Array-0jgVjd-W.js";var i=Object.create,b=function(){function e(){}return function(r){if(!p(r))return{};if(i)return i(r);e.prototype=r;var n=new e;return e.prototype=void 0,n}}();function z(e,r){var n=-1,t=e.length;for(r||(r=Array(t));++n<t;)r[n]=e[n];return r}function M(e,r,n,t){var h=!n;n||(n={});for(var u=-1,g=r.length;++u<g;){var s=r[u],o=void 0;o===void 0&&(o=e[s]),h?y(n,s,o):O(n,s,o)}return n}function A(e){var r=[];if(e!=null)for(var n in Object(e))r.push(n);return r}var T=Object.prototype,U=T.hasOwnProperty;function C(e){if(!p(e))return A(e);var r=v(e),n=[];for(var t in e)t=="constructor"&&(r||!U.call(e,t))||n.push(t);return n}function N(e){return x(e)?w(e,!0):C(e)}var I=P(Object.getPrototypeOf,Object),d=typeof exports=="object"&&exports&&!exports.nodeType&&exports,f=d&&typeof module=="object"&&module&&!module.nodeType&&module,K=f&&f.exports===d,c=K?m.Buffer:void 0,l=c?c.allocUnsafe:void 0;function R(e,r){if(r)return e.slice();var n=e.length,t=l?l(n):new e.constructor(n);return e.copy(t),t}function L(e){var r=new e.constructor(e.byteLength);return new a(r).set(new a(e)),r}function k(e,r){var n=r?L(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function q(e){return typeof e.constructor=="function"&&!v(e)?b(I(e)):{}}export{L as a,k as b,M as c,z as d,R as e,I as g,q as i,N as k};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-GL_nEn60.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-Jr-mDeWj.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./account-adjust.vue_vue_type_script_setup_true_lang-BOrrUt-H.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";export{o as default};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{d as h,s as v,j as q,c as B,V as w,I as V,o as I,C as j,w as r,b as F,m as a,e as n,p as s,t as y,E as D}from"./index-B2xNDy79.js";import{E as P,a as S}from"./el-form-item-DlU85AZK.js";/* empty css */import{E as U,a as G}from"./el-radio-CKcO4hVq.js";import{P as M}from"./index-DFOp_83R.js";const T={class:"pr-8"},L=h({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:E}){const u=v(),i=d,f=E,o=q({action:1,num:"",remark:""}),m=v(),c=B(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),k={num:[{required:!0,message:"请输入调整的金额"}]},R=e=>{if(e.includes("-"))return V.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=u.value)==null?void 0:e.validate()),f("confirm",o)},C=()=>{var e;f("update:show",!1),(e=u.value)==null||e.resetFields()};return w(()=>i.show,e=>{var t,l;e?(t=m.value)==null||t.open():(l=m.value)==null||l.close()}),w(c,e=>{e<0&&(V.msgError("调整后余额需大于0"),o.num="")}),(e,t)=>{const l=P,_=U,g=G,b=D,N=S;return I(),j(M,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:C},{default:r(()=>[F("div",T,[a(N,{ref_key:"formRef",ref:u,model:n(o),"label-width":"120px",rules:k},{default:r(()=>[a(l,{label:"当前余额"},{default:r(()=>[s("¥ "+y(d.value),1)]),_:1}),a(l,{label:"余额增减",required:"",prop:"action"},{default:r(()=>[a(g,{modelValue:n(o).action,"onUpdate:modelValue":t[0]||(t[0]=p=>n(o).action=p)},{default:r(()=>[a(_,{value:1},{default:r(()=>t[2]||(t[2]=[s("增加余额")])),_:1}),a(_,{value:2},{default:r(()=>t[3]||(t[3]=[s("扣减余额")])),_:1})]),_:1},8,["modelValue"])]),_:1}),a(l,{label:"调整余额",prop:"num"},{default:r(()=>[a(b,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:R},null,8,["model-value"])]),_:1}),a(l,{label:"调整后余额"},{default:r(()=>[s(" ¥ "+y(n(c)),1)]),_:1}),a(l,{label:"备注",prop:"remark"},{default:r(()=>[a(b,{modelValue:n(o).remark,"onUpdate:modelValue":t[1]||(t[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{L as _};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{D as h,B,G as q,H as j,C as D}from"./element-plus-Caccz9g_.js";import{_ as F}from"./index-Jr-mDeWj.js";import{f as v}from"./index-Ssy6AHhX.js";import{d as I,S as w,a8 as S,b as G,w as y,j as U,o as H,s as l,a as M,l as t,u as n,a5 as u,m as V}from"./@vue-DqCYILjk.js";const T={class:"pr-8"},L=I({__name:"account-adjust",props:{show:{type:Boolean,required:!0},value:{type:[Number,String],required:!0}},emits:["update:show","confirm"],setup(d,{emit:k}){const s=w(),i=d,f=k,o=S({action:1,num:"",remark:""}),m=w(),c=G(()=>Number(i.value)+Number(o.num)*(o.action==1?1:-1)),R={num:[{required:!0,message:"请输入调整的金额"}]},g=e=>{if(e.includes("-"))return v.msgError("请输入正整数");o.num=e},x=async()=>{var e;await((e=s.value)==null?void 0:e.validate()),f("confirm",o)},C=()=>{var e;f("update:show",!1),(e=s.value)==null||e.resetFields()};return y(()=>i.show,e=>{var a,r;e?(a=m.value)==null||a.open():(r=m.value)==null||r.close()}),y(c,e=>{e<0&&(v.msgError("调整后余额需大于0"),o.num="")}),(e,a)=>{const r=B,_=j,E=q,b=D,N=h;return H(),U(F,{ref_key:"popupRef",ref:m,title:"余额调整",width:"500px",onConfirm:x,async:!0,onClose:C},{default:l(()=>[M("div",T,[t(N,{ref_key:"formRef",ref:s,model:n(o),"label-width":"120px",rules:R},{default:l(()=>[t(r,{label:"当前余额"},{default:l(()=>[u("¥ "+V(d.value),1)]),_:1}),t(r,{label:"余额增减",required:"",prop:"action"},{default:l(()=>[t(E,{modelValue:n(o).action,"onUpdate:modelValue":a[0]||(a[0]=p=>n(o).action=p)},{default:l(()=>[t(_,{value:1},{default:l(()=>a[2]||(a[2]=[u("增加余额")])),_:1}),t(_,{value:2},{default:l(()=>a[3]||(a[3]=[u("扣减余额")])),_:1})]),_:1},8,["modelValue"])]),_:1}),t(r,{label:"调整余额",prop:"num"},{default:l(()=>[t(b,{"model-value":n(o).num,placeholder:"请输入调整的金额",type:"number",onInput:g},null,8,["model-value"])]),_:1}),t(r,{label:"调整后余额"},{default:l(()=>[u(" ¥ "+V(n(c)),1)]),_:1}),t(r,{label:"备注",prop:"remark"},{default:l(()=>[t(b,{modelValue:n(o).remark,"onUpdate:modelValue":a[1]||(a[1]=p=>n(o).remark=p),type:"textarea",rows:4},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])])]),_:1},512)}}});export{L as _};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-BDhrxmOG.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./add-nav.vue_vue_type_script_setup_true_lang-BlUx6EnP.js";import"./index-B2xNDy79.js";import"./index-BuNto3DN.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{C as E,B,g as C,i as N}from"./element-plus-Caccz9g_.js";import{_ as $}from"./index-DiKX45ze.js";import{_ as z}from"./picker-LVWrdhbz.js";import{_ as D}from"./picker-BtEFM3Ue.js";import{b as A,f as p}from"./index-Ssy6AHhX.js";import{D as I}from"./vuedraggable-B16X0h98.js";import{d as R,b as j,c as F,o as r,a as l,l as a,u as c,K,s as d,j as L,a5 as P}from"./@vue-DqCYILjk.js";const S={class:"bg-fill-light flex items-center w-full p-4 mb-4"},T={class:"upload-btn w-[60px] h-[60px]"},q={class:"ml-3 flex-1"},G={class:"flex items-center"},H={class:"flex items-center mt-[18px]"},J={class:"flex-1 flex items-center"},M={class:"drag-move cursor-move ml-auto"},le=R({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=j({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):p.msgError(`最多添加${t.max}个`)},g=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return p.msgError(`最少保留${t.min}个`);m.value.splice(s,1)};return(s,e)=>{const u=A,v=D,h=E,b=z,k=C,w=B,y=$,U=N;return r(),F("div",null,[l("div",null,[a(c(I),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>K(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:i})=>[(r(),L(y,{class:"w-[467px]",key:i,onClose:n=>g(i)},{default:d(()=>[l("div",S,[a(v,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",T,[a(u,{name:"el-icon-Plus",size:20})])]),_:2},1032,["modelValue","onUpdate:modelValue"]),l("div",q,[l("div",G,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(h,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",H,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(b,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",J,[a(k,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",M,[a(u,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(U,{type:"primary",onClick:x},{default:d(()=>e[3]||(e[3]=[P("添加")])),_:1})])])}}});export{le as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as U,c as C,o as p,a as B,b as l,m as a,w as d,C as N,e as c,D as $,p as D,I as r,q as z,E as I,J as A,v as R}from"./index-B2xNDy79.js";import{_ as q}from"./index-BuNto3DN.js";import{E as F}from"./el-form-item-DlU85AZK.js";import{_ as J}from"./picker-qQ9YEtJl.js";import{D as L,_ as P}from"./picker-Cd5l2hZ5.js";const S={class:"bg-fill-light flex items-center w-full p-4 mb-4"},T={class:"upload-btn w-[60px] h-[60px]"},j={class:"ml-3 flex-1"},G={class:"flex items-center"},H={class:"flex items-center mt-[18px]"},K={class:"flex-1 flex items-center"},M={class:"drag-move cursor-move ml-auto"},Z=U({__name:"add-nav",props:{modelValue:{type:Array,default:()=>[]},max:{type:Number,default:100},min:{type:Number,default:1}},emits:["update:modelValue"],setup(_,{emit:f}){const t=_,V=f,m=C({get(){return t.modelValue},set(s){V("update:modelValue",s)}}),x=()=>{var s;((s=t.modelValue)==null?void 0:s.length)<t.max?m.value.push({image:"",name:"导航名称",link:{},is_show:"1"}):r.msgError(`最多添加${t.max}个`)},v=s=>{var e;if(((e=t.modelValue)==null?void 0:e.length)<=t.min)return r.msgError(`最少保留${t.min}个`);m.value.splice(s,1)};return(s,e)=>{const u=z,g=P,h=I,k=J,b=A,w=F,y=q,E=R;return p(),B("div",null,[l("div",null,[a(c(L),{class:"draggable",modelValue:c(m),"onUpdate:modelValue":e[0]||(e[0]=o=>$(m)?m.value=o:null),animation:"300",handle:".drag-move","item-key":"index"},{item:d(({element:o,index:i})=>[(p(),N(y,{class:"w-[467px]",key:i,onClose:n=>v(i)},{default:d(()=>[l("div",S,[a(g,{modelValue:o.image,"onUpdate:modelValue":n=>o.image=n,"upload-class":"bg-body",size:"60px","exclude-domain":""},{upload:d(()=>[l("div",T,[a(u,{name:"el-icon-Plus",size:20})])]),_:2},1032,["modelValue","onUpdate:modelValue"]),l("div",j,[l("div",G,[e[1]||(e[1]=l("span",{class:"text-tx-regular flex-none mr-3"},"名称",-1)),a(h,{modelValue:o.name,"onUpdate:modelValue":n=>o.name=n,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),l("div",H,[e[2]||(e[2]=l("span",{class:"text-tx-regular flex-none mr-3"},"链接",-1)),a(k,{modelValue:o.link,"onUpdate:modelValue":n=>o.link=n},null,8,["modelValue","onUpdate:modelValue"])]),a(w,{label:"是否显示",class:"mt-[18px]"},{default:d(()=>[l("div",K,[a(b,{modelValue:o.is_show,"onUpdate:modelValue":n=>o.is_show=n,"active-value":"1","inactive-value":"0"},null,8,["modelValue","onUpdate:modelValue"]),l("div",M,[a(u,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])])]),_:2},1032,["onClose"]))]),_:1},8,["modelValue"])]),l("div",null,[a(E,{type:"primary",onClick:x},{default:d(()=>e[3]||(e[3]=[D("添加")])),_:1})])])}}});export{Z as _};
|
||||
@@ -0,0 +1 @@
|
||||
import{r as n}from"./index-Ssy6AHhX.js";function e(t){return n.get({url:"/auth.admin/lists",params:t},{ignoreCancelToken:!0})}function i(t){return n.post({url:"/auth.admin/add",params:t})}function r(t){return n.post({url:"/auth.admin/edit",params:t})}function u(t){return n.post({url:"/auth.admin/delete",params:t})}function d(t){return n.get({url:"/auth.admin/detail",params:t})}export{e as a,r as b,i as c,d,u as e};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{y as e}from"./index-B2xNDy79.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{u as a,s as b,a as c,c as d,i as e,n as f,p as g,l as h,f as i,d as j,g as k,C as l,o as m};
|
||||
import{r as e}from"./index-Ssy6AHhX.js";function a(t){return e.get({url:"/article.articleCate/lists",params:t})}function l(t){return e.get({url:"/article.articleCate/all",params:t})}function i(t){return e.post({url:"/article.articleCate/add",params:t})}function c(t){return e.post({url:"/article.articleCate/edit",params:t})}function u(t){return e.post({url:"/article.articleCate/delete",params:t})}function n(t){return e.get({url:"/article.articleCate/detail",params:t})}function s(t){return e.post({url:"/article.articleCate/updateStatus",params:t})}function o(t){return e.get({url:"/article.article/lists",params:t})}function d(t){return e.post({url:"/article.article/add",params:t})}function f(t){return e.post({url:"/article.article/edit",params:t})}function C(t){return e.post({url:"/article.article/delete",params:t})}function p(t){return e.get({url:"/article.article/detail",params:t})}function g(t){return e.post({url:"/article.article/updateStatus",params:t})}export{a,u as b,s as c,c as d,i as e,n as f,p as g,f as h,d as i,l as j,o as k,g as l,C as m};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BOqi3ZCD.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BDhrxmOG.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-Dr4vyFK-.js";import"./@vue-DqCYILjk.js";export{m as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DD7J54WS.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./el-card-DpH4mUSc.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-D3n1kY8a.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./el-card-DpH4mUSc.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BlUx6EnP.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-AMBSJ9oX.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";import"./index.vue_vue_type_script_setup_true_lang-C-xtr9xT.js";import"./el-card-DpH4mUSc.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{m as b,l as c,D as V}from"./element-plus-Caccz9g_.js";import{_ as l}from"./menu-set.vue_vue_type_script_setup_true_lang-g1MqaIBB.js";import{d as v,b as x,c as E,o as g,a as k,l as o,s as m,u as r,F as w}from"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";const bt=v({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(n,{emit:s}){const u=n,d=s,e=x({get(){return u.modelValue},set(a){d("update:modelValue",a)}});return(a,t)=>{const i=c,f=b,_=V;return g(),E(w,null,[t[2]||(t[2]=k("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),o(_,{class:"mt-4","label-width":"70px"},{default:m(()=>[o(f,{"model-value":"nav"},{default:m(()=>[o(i,{label:"主导航设置",name:"nav"},{default:m(()=>[o(l,{modelValue:r(e).nav,"onUpdate:modelValue":t[0]||(t[0]=p=>r(e).nav=p)},null,8,["modelValue"])]),_:1}),o(i,{label:"菜单设置",name:"menu"},{default:m(()=>[o(l,{modelValue:r(e).menu,"onUpdate:modelValue":t[1]||(t[1]=p=>r(e).menu=p)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{bt as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-BfAYpuFH.js";import"./index-B2xNDy79.js";export{m as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-de_3-WPc.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./picker-BtEFM3Ue.js";import"./index-Jr-mDeWj.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./index-BwD-9drg.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./index-DiKX45ze.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./usePaging-BeX7EvMy.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-DPlJr_2P.js";import"./@vue-DqCYILjk.js";export{m as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BOzseOOT.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as b,c,o as V,a as v,b as x,m as t,w as m,e as a,F as w,a1 as E,a2 as g}from"./index-B2xNDy79.js";import{a as k}from"./el-form-item-DlU85AZK.js";import{_ as n}from"./menu-set.vue_vue_type_script_setup_true_lang-BpSpHdih.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";const Z=b({__name:"attr",props:{modelValue:{type:Object,default:()=>({nav:[],menu:{}})}},emits:["update:modelValue"],setup(i,{emit:s}){const u=i,d=s,o=c({get(){return u.modelValue},set(l){d("update:modelValue",l)}});return(l,e)=>{const p=E,f=g,_=k;return V(),v(w,null,[e[2]||(e[2]=x("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"}," pc导航设置 ",-1)),t(_,{class:"mt-4","label-width":"70px"},{default:m(()=>[t(f,{"model-value":"nav"},{default:m(()=>[t(p,{label:"主导航设置",name:"nav"},{default:m(()=>[t(n,{modelValue:a(o).nav,"onUpdate:modelValue":e[0]||(e[0]=r=>a(o).nav=r)},null,8,["modelValue"])]),_:1}),t(p,{label:"菜单设置",name:"menu"},{default:m(()=>[t(n,{modelValue:a(o).menu,"onUpdate:modelValue":e[1]||(e[1]=r=>a(o).menu=r)},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})],64)}}});export{Z as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-Cv1wbPt-.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./el-card-DpH4mUSc.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BlUx6EnP.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-CrB_m0pI.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index.vue_vue_type_script_setup_true_lang-xkN7p-rq.js";import"./@vueuse-DAQYQaJJ.js";import"./picker-BtEFM3Ue.js";import"./index-Jr-mDeWj.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./index-BwD-9drg.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./index-DiKX45ze.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./usePaging-BeX7EvMy.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-bDu15G_R.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./el-card-DpH4mUSc.js";import"./index.vue_vue_type_script_setup_true_lang-C-xtr9xT.js";import"./picker-Cd5l2hZ5.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index-BhVAe0P7.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./index-BuNto3DN.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-DyaVW45d.js";import"./index-B2xNDy79.js";export{m as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-DewLrFgL.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BDhrxmOG.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-_e1mwglx.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./el-card-DpH4mUSc.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-Bq6XRs47.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-DkUoKAk4.js";import"./index-B2xNDy79.js";export{m as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{_ as m}from"./attr.vue_vue_type_script_setup_true_lang-D3IuAQ6Y.js";import"./@vue-DqCYILjk.js";export{m as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
.banner-upload-btn[data-v-3e6394fe]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;border-style:dashed;border-color:var(--el-border-color);color:var(--el-text-color-secondary)}
|
||||
.banner-upload-btn[data-v-f10c74d9]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:.25rem;border-width:1px;border-style:dashed;border-color:var(--el-border-color);color:var(--el-text-color-secondary)}
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-BUnp-W91.js";import"./index-B2xNDy79.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./el-card-DpH4mUSc.js";import"./picker-Cd5l2hZ5.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index-BhVAe0P7.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./index-BuNto3DN.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";import"./usePaging-Dm2wALfy.js";export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-BQM53w0Z.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DmF0RBWa.js";import"./attr-iq0g5OCc.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";import"./content.vue_vue_type_script_setup_true_lang-BamgCqgy.js";import"./decoration-img-BZXyfkpJ.js";import"./attr.vue_vue_type_script_setup_true_lang-de_3-WPc.js";import"./content-C6cFqWoQ.js";import"./attr.vue_vue_type_script_setup_true_lang-BOzseOOT.js";import"./content.vue_vue_type_script_setup_true_lang-H9cPfbIM.js";import"./attr.vue_vue_type_script_setup_true_lang-BOqi3ZCD.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BDhrxmOG.js";import"./content-ghDmjcU5.js";import"./attr.vue_vue_type_script_setup_true_lang-DewLrFgL.js";import"./content.vue_vue_type_script_setup_true_lang-DH3woB_a.js";import"./attr.vue_vue_type_script_setup_true_lang-DPlJr_2P.js";import"./content-PvNfK7J-.js";import"./decoration-DD-ajGy1.js";import"./attr.vue_vue_type_script_setup_true_lang-CrB_m0pI.js";import"./index.vue_vue_type_script_setup_true_lang-xkN7p-rq.js";import"./content-Ck3-fFiT.js";import"./content.vue_vue_type_script_setup_true_lang-DjuQ15hR.js";import"./attr.vue_vue_type_script_setup_true_lang-Dr4vyFK-.js";import"./content-NpkvqLX7.js";import"./attr.vue_vue_type_script_setup_true_lang-Bq6XRs47.js";import"./content.vue_vue_type_script_setup_true_lang-Czl-F9_j.js";import"./attr.vue_vue_type_script_setup_true_lang-D3IuAQ6Y.js";import"./content-YU36KcEj.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as o}from"./attr-setting.vue_vue_type_script_setup_true_lang-BTvRoJfb.js";import"./index-B2xNDy79.js";import"./el-card-DpH4mUSc.js";import"./index-JqFibg1v.js";import"./attr-Bqhk7AF3.js";import"./el-form-item-DlU85AZK.js";import"./_baseClone-CdezRMKA.js";import"./_Uint8Array-0jgVjd-W.js";import"./_initCloneObject-C-h6JGU9.js";import"./index-BuNto3DN.js";import"./picker-qQ9YEtJl.js";import"./index-DFOp_83R.js";import"./index-C6Cr8aHe.js";import"./index.vue_vue_type_script_setup_true_lang-DUdeBZfj.js";import"./el-tag-CuODyGk4.js";import"./isEqual-CLGO95LP.js";import"./el-select-BRdnbwTl.js";import"./index-CcX0CyWL.js";import"./token-DI9FKtlJ.js";import"./el-table-column-DG3vRCd5.js";import"./el-checkbox-3_Bu4Dnb.js";import"./article-Dwgm3r-g.js";import"./usePaging-Dm2wALfy.js";/* empty css */import"./el-radio-CKcO4hVq.js";import"./picker-Cd5l2hZ5.js";import"./index-BhVAe0P7.js";import"./index-DSiy6YVt.js";import"./el-tree-8o9N7gsQ.js";import"./index.vue_vue_type_script_setup_true_lang-B8J7_re8.js";import"./el-popover-Bpu4paqp.js";import"./content.vue_vue_type_script_setup_true_lang-DchKkk1p.js";import"./decoration-img-2F0tdl1c.js";import"./attr.vue_vue_type_script_setup_true_lang-BUnp-W91.js";import"./content-CA3eC6JD.js";import"./attr.vue_vue_type_script_setup_true_lang-_e1mwglx.js";import"./content.vue_vue_type_script_setup_true_lang-BnJOM-Yt.js";import"./attr.vue_vue_type_script_setup_true_lang-D3n1kY8a.js";import"./add-nav.vue_vue_type_script_setup_true_lang-BlUx6EnP.js";import"./content-D3wpKzNH.js";import"./attr.vue_vue_type_script_setup_true_lang-Cv1wbPt-.js";import"./content.vue_vue_type_script_setup_true_lang-CwpJZnDJ.js";import"./attr.vue_vue_type_script_setup_true_lang-DyaVW45d.js";import"./content-CVqMLU2z.js";import"./decoration-C6Bzwzfj.js";import"./attr.vue_vue_type_script_setup_true_lang-bDu15G_R.js";import"./index.vue_vue_type_script_setup_true_lang-C-xtr9xT.js";import"./content-DRWnLeOw.js";import"./content.vue_vue_type_script_setup_true_lang-B_7xmKd0.js";import"./el-alert-BUxHh72o.js";import"./attr.vue_vue_type_script_setup_true_lang-DkUoKAk4.js";import"./content-CvSg00F8.js";import"./attr.vue_vue_type_script_setup_true_lang-DD7J54WS.js";import"./content.vue_vue_type_script_setup_true_lang-DXwxhIWi.js";import"./attr.vue_vue_type_script_setup_true_lang-BfAYpuFH.js";import"./content-DdyiS-ri.js";export{o as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{I as g,s as y}from"./element-plus-Caccz9g_.js";import{e as b}from"./index-DmF0RBWa.js";import{d as x,c as _,o as a,l as c,s as r,a as h,m as w,j as i,aG as v,a4 as C,u as k}from"./@vue-DqCYILjk.js";const B={class:"pages-setting"},E={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},U=x({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:d}){const m=d,p=n=>{m("update:content",n)};return(n,S)=>{const f=g,u=y;return a(),_("div",B,[c(f,{shadow:"never",class:"!border-none flex"},{default:r(()=>{var t;return[h("div",E,w((t=e.widget)==null?void 0:t.title),1)]}),_:1}),c(u,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:r(()=>{var t,o,s,l;return[(a(),i(v,null,[(a(),i(C((o=k(b)[(t=e.widget)==null?void 0:t.name])==null?void 0:o.attr),{content:(s=e.widget)==null?void 0:s.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{U as _};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{d as u,o as n,a as g,m as c,w as r,b as y,t as x,C as i,cp as _,aq as w,e as h,bJ as C}from"./index-B2xNDy79.js";import{E as v}from"./el-card-DpH4mUSc.js";import{e as k}from"./index-JqFibg1v.js";const B={class:"pages-setting"},E={class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},V=u({__name:"attr-setting",props:{widget:{type:Object,default:()=>({})},type:{type:String,default:"mobile"}},emits:["update:content"],setup(e,{emit:d}){const m=d,p=o=>{m("update:content",o)};return(o,S)=>{const f=v,b=C;return n(),g("div",B,[c(f,{shadow:"never",class:"!border-none flex"},{default:r(()=>{var t;return[y("div",E,x((t=e.widget)==null?void 0:t.title),1)]}),_:1}),c(b,{class:"w-full",style:{height:"calc(100% - 60px)"}},{default:r(()=>{var t,a,s,l;return[(n(),i(_,null,[(n(),i(w((a=h(k)[(t=e.widget)==null?void 0:t.name])==null?void 0:a.attr),{content:(s=e.widget)==null?void 0:s.content,styles:(l=e.widget)==null?void 0:l.styles,type:e.type,"onUpdate:content":p},null,40,["content","styles","type"]))],1024))]}),_:1})])}}});export{V as _};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as o}from"./attr.vue_vue_type_script_setup_true_lang-d1tYut2l.js";import"./element-plus-Caccz9g_.js";import"./@vue-DqCYILjk.js";import"./@element-plus-DUVk59Vi.js";import"./lodash-es-B-ecsAzo.js";import"./dayjs-DwMCULLY.js";import"./@popperjs-D_chPuIy.js";import"./async-validator-9PlIezaS.js";import"./@ctrl-r5W6hzzQ.js";import"./normalize-wheel-es-BQoi3Ox2.js";import"./index-DiKX45ze.js";import"./index-Ssy6AHhX.js";import"./nprogress-Cn18nAuv.js";import"./vue-router-B-0K5V-K.js";import"./pinia-mEEzQJvk.js";import"./axios-DIsA03vz.js";import"./lodash-y93ab0Xv.js";import"./@vueuse-DAQYQaJJ.js";import"./css-color-function-Oslqczna.js";import"./balanced-match-BdS7OldZ.js";import"./color-DYO1E7y4.js";import"./clone-Ddk2tjDh.js";import"./color-convert-DsNsD289.js";import"./color-name-Dju3oUBS.js";import"./color-string-pUQDDJII.js";import"./ms-CzQ2E3wO.js";import"./vue-clipboard3-CdRib5DX.js";import"./clipboard-DbF4rXjn.js";import"./echarts-CXZcVnUo.js";import"./tslib-BDyQ-Jie.js";import"./zrender-DO4NV0px.js";import"./highlight.js-CCvalMLC.js";import"./@highlightjs-Bvtfkhq1.js";import"./picker-LVWrdhbz.js";import"./index-Jr-mDeWj.js";import"./index.vue_vue_type_script_setup_true_lang-V4d89ezW.js";import"./article-B3Pk9yTH.js";import"./usePaging-BeX7EvMy.js";import"./picker-BtEFM3Ue.js";import"./index-BwD-9drg.js";import"./index-C4EL5Y1u.js";import"./index.vue_vue_type_script_setup_true_lang-DXGkF5uD.js";import"./vuedraggable-B16X0h98.js";import"./vue-UlV55hVW.js";import"./sortablejs-BRHf-Xth.js";import"./index.vue_vue_type_script_setup_true_lang-xkN7p-rq.js";export{o as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as q,c as V,o as v,a as w,m as e,w as t,b as a,p as h,e as m,Y as A,t as y,G,F as J,I as x,q as L,E as M,J as O,v as R}from"./index-B2xNDy79.js";import{E as Y,a as H}from"./el-form-item-DlU85AZK.js";import{_ as K}from"./index-BuNto3DN.js";import{_ as Q}from"./picker-qQ9YEtJl.js";import{D as W,_ as X}from"./picker-Cd5l2hZ5.js";import{_ as Z}from"./index.vue_vue_type_script_setup_true_lang-C-xtr9xT.js";import{E as ee}from"./el-card-DpH4mUSc.js";const le={class:"mb-[18px] max-w-[400px]"},oe={class:"bg-fill-light w-full p-4 mt-4"},te={class:"upload-btn w-[60px] h-[60px]"},ae={class:"upload-btn w-[60px] h-[60px]"},se={class:"flex-1 flex items-center"},ne={class:"drag-move cursor-move ml-auto"},de={key:0,class:"mt-4"},c=5,p=2,fe=q({__name:"attr",props:{modelValue:{type:Object,default:()=>({list:[],style:{}})}},emits:["update:modelValue"],setup(k,{emit:E}){const U=k,C=E,n=V({get(){return U.modelValue},set(s){C("update:modelValue",s)}}),$=V(()=>{var s;return((s=n.value.list)==null?void 0:s.filter(l=>l.is_show=="1"))||[]}),z=()=>{var s;((s=n.value.list)==null?void 0:s.length)<c?n.value.list.push({name:"",selected:"",unselected:"",is_show:1,link:{}}):x.msgError(`最多添加${c}个`)},B=s=>{var l;if(((l=n.value.list)==null?void 0:l.length)<=p)return x.msgError(`最少保留${p}个`);n.value.list.splice(s,1)},D=s=>s.relatedContext.index!=0,F=s=>{if($.value.length<p)return s.is_show=1,x.msgError(`最少显示${p}个`)};return(s,l)=>{const _=ee,b=Z,i=Y,f=L,g=X,N=M,I=Q,S=O,P=K,T=R,j=H;return v(),w(J,null,[e(_,{shadow:"never",class:"!border-none flex"},{default:t(()=>l[3]||(l[3]=[a("div",{class:"title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2 text-xl font-medium"},[h(" 底部导航设置 "),a("span",{class:"form-tips ml-[10px] !mt-0"}," 至少添加2个导航,最多添加5个导航 ")],-1)])),_:1}),e(j,{"label-width":"70px"},{default:t(()=>[e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>[l[4]||(l[4]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),e(i,{label:"默认颜色"},{default:t(()=>[e(b,{class:"max-w-[400px]",modelValue:m(n).style.default_color,"onUpdate:modelValue":l[0]||(l[0]=u=>m(n).style.default_color=u),"default-color":"#999999"},null,8,["modelValue"])]),_:1}),e(i,{label:"选中颜色",style:{"margin-bottom":"0"}},{default:t(()=>[e(b,{class:"max-w-[400px]",modelValue:m(n).style.selected_color,"onUpdate:modelValue":l[1]||(l[1]=u=>m(n).style.selected_color=u),"default-color":"#4173ff"},null,8,["modelValue"])]),_:1})]),_:1}),e(_,{shadow:"never",class:"!border-none flex mt-2"},{default:t(()=>{var u;return[l[7]||(l[7]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单设置"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",le,[e(m(W),{class:"draggable",modelValue:m(n).list,"onUpdate:modelValue":l[2]||(l[2]=o=>m(n).list=o),animation:"300",draggable:".draggable",handle:".drag-move",move:D,"item-key":"index"},{item:t(({element:o,index:r})=>[e(P,{onClose:d=>B(r),class:A(["max-w-[400px]",{draggable:r!=0}]),"show-close":r!==0},{default:t(()=>[a("div",oe,[e(i,{label:"导航图标"},{default:t(()=>[e(g,{modelValue:o.unselected,"onUpdate:modelValue":d=>o.unselected=d,"upload-class":"bg-body","exclude-domain":"",size:"60px"},{upload:t(()=>[a("div",te,[e(f,{name:"el-icon-Plus",size:16}),l[5]||(l[5]=a("span",{class:"text-xs leading-5"}," 未选中 ",-1))])]),_:2},1032,["modelValue","onUpdate:modelValue"]),e(g,{modelValue:o.selected,"onUpdate:modelValue":d=>o.selected=d,"exclude-domain":"","upload-class":"bg-body",size:"60px"},{upload:t(()=>[a("div",ae,[e(f,{name:"el-icon-Plus",size:16}),l[6]||(l[6]=a("span",{class:"text-xs leading-5"}," 选中 ",-1))])]),_:2},1032,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"导航名称"},{default:t(()=>[e(N,{modelValue:o.name,"onUpdate:modelValue":d=>o.name=d,placeholder:"请输入名称"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"链接地址"},{default:t(()=>[e(I,{"is-tab":!0,disabled:r===0,modelValue:o.link,"onUpdate:modelValue":d=>o.link=d},null,8,["disabled","modelValue","onUpdate:modelValue"])]),_:2},1024),e(i,{label:"是否显示"},{default:t(()=>[a("div",se,[e(S,{disabled:r==0,modelValue:o.is_show,"onUpdate:modelValue":d=>o.is_show=d,"active-value":1,"inactive-value":0,onChange:d=>F(o)},null,8,["disabled","modelValue","onUpdate:modelValue","onChange"]),a("div",ne,[e(f,{name:"el-icon-Rank",size:"18"})])])]),_:2},1024)])]),_:2},1032,["onClose","show-close","class"])]),_:1},8,["modelValue"])]),((u=m(n).list)==null?void 0:u.length)<c?(v(),w("div",de,[e(T,{class:"w-full",type:"primary",onClick:z},{default:t(()=>{var o;return[h(" 添加导航 "+y((o=m(n).list)==null?void 0:o.length)+" / "+y(c),1)]}),_:1})])):G("",!0)]}),_:1})]),_:1})],64)}}});export{fe as _};
|
||||
@@ -0,0 +1 @@
|
||||
import{I as V,B as E,C as w,G as y,H as B,D as C}from"./element-plus-Caccz9g_.js";import{_ as I}from"./add-nav.vue_vue_type_script_setup_true_lang-BDhrxmOG.js";import{d as N,b as U,c as g,o as j,l as t,s as o,u as s,a,a5 as u}from"./@vue-DqCYILjk.js";const k={class:"flex-1"},O=N({__name:"attr",props:{content:{type:Object,default:()=>({})},styles:{type:Object,default:()=>({})}},emits:["update:content"],setup(p,{emit:i}){const f=i,_=p,l=U({get:()=>_.content,set:m=>{f("update:content",m)}});return(m,e)=>{const x=w,c=E,d=V,r=B,b=y,v=C;return j(),g("div",null,[t(v,{"label-width":"70px"},{default:o(()=>[t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[t(c,{label:"标题"},{default:o(()=>[t(x,{class:"w-[396px]",modelValue:s(l).title,"onUpdate:modelValue":e[0]||(e[0]=n=>s(l).title=n)},null,8,["modelValue"])]),_:1})]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[5]||(e[5]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"展示样式")],-1)),t(b,{modelValue:s(l).style,"onUpdate:modelValue":e[1]||(e[1]=n=>s(l).style=n)},{default:o(()=>[t(r,{value:1},{default:o(()=>e[3]||(e[3]=[u("横排")])),_:1}),t(r,{value:2},{default:o(()=>e[4]||(e[4]=[u("竖排")])),_:1})]),_:1},8,["modelValue"])]),_:1}),t(d,{shadow:"never",class:"!border-none flex mt-2"},{default:o(()=>[e[6]||(e[6]=a("div",{class:"flex items-end mb-4"},[a("div",{class:"text-base text-[#101010] font-medium"},"菜单"),a("div",{class:"text-xs text-tx-secondary ml-2"},"建议图片尺寸:100px*100px")],-1)),a("div",k,[t(I,{modelValue:s(l).data,"onUpdate:modelValue":e[2]||(e[2]=n=>s(l).data=n)},null,8,["modelValue"])])]),_:1})]),_:1})])}}});export{O as _};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user